QbeGen.mod 114 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881
  1. IMPLEMENTATION MODULE QbeGen;
  2. IMPORT FileIO, SymTab;
  3. VAR
  4. out : FileIO.File;
  5. opened : BOOLEAN;
  6. inBody : BOOLEAN;
  7. dead : BOOLEAN; (* TRUE past a terminator: next instruction opens
  8. an unreachable block with a fresh label, so
  9. the .ssa stays valid QBE (no dangling temps,
  10. no instruction outside a block). *)
  11. nTemp : CARDINAL;
  12. nLab : CARDINAL;
  13. loopTop : CARDINAL;
  14. loopSt : ARRAY [0 .. 15] OF QVal;
  15. nR : CARDINAL; (* non-zero REAL consts flushed at BeginBody *)
  16. rNames : ARRAY [0 .. 31] OF QVal;
  17. rVals : ARRAY [0 .. 31] OF QVal;
  18. nStr : CARDINAL; (* string-literal data counter *)
  19. strNams : ARRAY [0 .. 4095] OF QVal;
  20. strTexts : ARRAY [0 .. 4095] OF ARRAY [0 .. 255] OF CHAR;
  21. (* UString literals: decoded codepoints, recorded for EndModule. *)
  22. ustrN : CARDINAL;
  23. ustrNams : ARRAY [0 .. 255] OF QVal;
  24. ustrStart : ARRAY [0 .. 255] OF CARDINAL;
  25. ustrCount : ARRAY [0 .. 255] OF CARDINAL;
  26. ustrPool : ARRAY [0 .. 8191] OF INTEGER;
  27. ustrUsed : CARDINAL;
  28. (* array constructors: pooled static descriptors *)
  29. ctorN : CARDINAL;
  30. ctorNam : ARRAY [0 .. 255] OF QVal;
  31. ctorTxt : ARRAY [0 .. 255] OF ARRAY [0 .. 4095] OF CHAR;
  32. ctorUse : ARRAY [0 .. 255] OF BOOLEAN; (* FALSE once inlined as a field *)
  33. ctorTop : CARDINAL;
  34. ctorTyp : ARRAY [0 .. 7] OF INTEGER;
  35. ctorCnt : ARRAY [0 .. 7] OF CARDINAL;
  36. ctorBuf : ARRAY [0 .. 7] OF ARRAY [0 .. 4095] OF CHAR;
  37. ctorEv : ARRAY [0 .. 7] OF ARRAY [0 .. 255] OF QVal;
  38. ctorEk : ARRAY [0 .. 7] OF ARRAY [0 .. 255] OF INTEGER;
  39. withTop : CARDINAL;
  40. withSt : ARRAY [0 .. 7] OF QVal;
  41. noEmit : BOOLEAN; (* TRUE while parsing nested procedures in 4.1:
  42. SymTab tracks, QbeGen suppresses output *)
  43. inFunc : BOOLEAN; (* TRUE inside an emitted function body *)
  44. useStack : BOOLEAN;(* TRUE: NewHeap allocates stack slots *)
  45. funcRes : CHAR; (* current function result class *)
  46. nLoc : CARDINAL; (* function-local name table entries *)
  47. locNames : ARRAY [0 .. 255] OF SymTab.Name;
  48. locTag : ARRAY [0 .. 255] OF INTEGER; (* 0 slot, 1 data, 2 addr *)
  49. locRep : ARRAY [0 .. 255] OF QVal; (* slot temp / mangled / addr *)
  50. locCls : ARRAY [0 .. 255] OF CHAR;
  51. locTyp : ARRAY [0 .. 255] OF INTEGER;
  52. funcDepth : CARDINAL; (* open BeginFuncs; main body = 0 *)
  53. scopeTop : CARDINAL; (* open procedure scopes *)
  54. scopeBase : ARRAY [0 .. 16] OF CARDINAL;
  55. scopeLink : ARRAY [0 .. 15] OF QVal; (* per-scope link records *)
  56. slTmp : QVal; (* current static-link param temp *)
  57. hdrComma : BOOLEAN;
  58. stkLink : ARRAY [0 .. 15] OF QVal; (* per-call static links *)
  59. stkExt : ARRAY [0 .. 15] OF BOOLEAN; (* per-call external flag *)
  60. stkInd : ARRAY [0 .. 15] OF BOOLEAN; (* per-call indirect flag *)
  61. outSel : CARDINAL; (* 0 = file, else nestBufs[outSel-1] *)
  62. nInit : CARDINAL;
  63. initNames : ARRAY [0 .. 31] OF QVal;
  64. sessBuf : ARRAY [0 .. 4194303] OF CHAR; (* whole image, buffered (4 MiB:
  65. must hold the compiler's own
  66. image text when self-hosting) *)
  67. sessUsed : CARDINAL;
  68. nestBufs : ARRAY [0 .. 15] OF ARRAY [0 .. 65535] OF CHAR;
  69. nestUsed : ARRAY [0 .. 15] OF CARDINAL;
  70. (* Short-circuit AND/OR: the RHS operand's code is buffered here
  71. (delayTop > 0) and replayed inside the branch. *)
  72. delayBuf : ARRAY [0 .. 15] OF ARRAY [0 .. 8191] OF CHAR;
  73. delayUsed : ARRAY [0 .. 15] OF CARDINAL;
  74. delayTop : CARDINAL;
  75. (* one buffer per nesting depth: each nested function stays
  76. contiguous no matter how deep the parse interleaves *)
  77. nPar : CARDINAL; (* recorded formal params for the header *)
  78. parCls : ARRAY [0 .. 63] OF CHAR;
  79. parTmp : ARRAY [0 .. 63] OF QVal;
  80. parNam : ARRAY [0 .. 63] OF SymTab.Name;
  81. parVar : ARRAY [0 .. 63] OF BOOLEAN;
  82. parTyp : ARRAY [0 .. 63] OF INTEGER;
  83. argBuf : ARRAY [0 .. 4095] OF CHAR; (* accumulated call args *)
  84. hbuf : ARRAY [0 .. 2047] OF CHAR; (* buffered function header *)
  85. nArg : CARDINAL;
  86. recvQ : QVal; (* armed class-method receiver *)
  87. recvArmed : BOOLEAN;
  88. callDepth : CARDINAL; (* nested-call stack *)
  89. stkName : ARRAY [0 .. 15] OF QVal;
  90. stkRes : ARRAY [0 .. 15] OF CHAR;
  91. stkArg : ARRAY [0 .. 15] OF ARRAY [0 .. 1023] OF CHAR;
  92. stkN : ARRAY [0 .. 15] OF CARDINAL;
  93. funcName : QVal; (* current function / call target *)
  94. curModName : SymTab.Name; (* module being compiled (global names) *)
  95. outName : SymTab.Name; (* image-file override (Lower compare) *)
  96. hasOutName : BOOLEAN;
  97. nVal : ARRAY [0 .. 255] OF QVal; (* VAR-actual note keys *)
  98. nAddr : ARRAY [0 .. 255] OF QVal; (* VAR-actual note addresses *)
  99. nn : CARDINAL;
  100. (* Forward-global designator patch sites: a not-yet-declared
  101. identifier inside a procedure body emits "site =l copy 0" plus a
  102. load/store operand "fwd<id>"; both are text-patched once the
  103. module's declarations are complete. *)
  104. nFwd : CARDINAL;
  105. dbgFwd : INTEGER;
  106. fwdSite : ARRAY [0 .. 255] OF SymTab.Name; (* real name to patch in *)
  107. fwdOper : ARRAY [0 .. 255] OF QVal; (* final load/store class *)
  108. fwdGlob : ARRAY [0 .. 255] OF BOOLEAN;
  109. (* Session-state snapshot: Lower runs per unit while the grammar's
  110. suppressed inline emit still mutates these, so Lower saves/restores
  111. its own values around each unit. *)
  112. sav_nTemp, sav_nLab, sav_loopTop, sav_nR, sav_nStr, sav_nLoc,
  113. sav_nPar, sav_nArg, sav_nn, sav_nInit, sav_sessUsed, sav_delayTop,
  114. sav_ustrN, sav_ustrUsed, sav_ctorN, sav_ctorTop, sav_funcDepth,
  115. sav_scopeTop, sav_callDepth, sav_outSel, sav_withTop : CARDINAL;
  116. sav_inBody, sav_inFunc, sav_useStack, sav_recvArmed : BOOLEAN;
  117. PROCEDURE SaveSession;
  118. BEGIN
  119. sav_nTemp := nTemp; sav_nLab := nLab; sav_loopTop := loopTop;
  120. sav_nR := nR; sav_nStr := nStr; sav_nLoc := nLoc; sav_nPar := nPar;
  121. sav_nArg := nArg; sav_nn := nn; sav_nInit := nInit;
  122. sav_sessUsed := sessUsed; sav_delayTop := delayTop;
  123. sav_ustrN := ustrN; sav_ustrUsed := ustrUsed;
  124. sav_ctorN := ctorN; sav_ctorTop := ctorTop;
  125. sav_funcDepth := funcDepth; sav_scopeTop := scopeTop;
  126. sav_callDepth := callDepth; sav_outSel := outSel;
  127. sav_withTop := withTop;
  128. sav_inBody := inBody; sav_inFunc := inFunc;
  129. sav_useStack := useStack; sav_recvArmed := recvArmed
  130. END SaveSession;
  131. PROCEDURE RestoreSession;
  132. VAR i : CARDINAL;
  133. BEGIN
  134. nTemp := sav_nTemp; nLab := sav_nLab; loopTop := sav_loopTop;
  135. nR := sav_nR; nStr := sav_nStr; nLoc := sav_nLoc; nPar := sav_nPar;
  136. nArg := sav_nArg; nInit := sav_nInit;
  137. sessUsed := sav_sessUsed; delayTop := sav_delayTop;
  138. ustrN := sav_ustrN; ustrUsed := sav_ustrUsed;
  139. ctorN := sav_ctorN; ctorTop := sav_ctorTop;
  140. funcDepth := sav_funcDepth; scopeTop := sav_scopeTop;
  141. callDepth := sav_callDepth; outSel := sav_outSel;
  142. withTop := sav_withTop;
  143. inBody := sav_inBody; inFunc := sav_inFunc;
  144. useStack := sav_useStack; recvArmed := sav_recvArmed;
  145. (* Clear the VAR-actual note ring. The grammar's suppressed
  146. parse-time emit never reaches the buffer, but its NoteAddr calls
  147. do fill this ring; AddrOfVal scans the whole ring and the keys
  148. are temps that restart per function, so a stale grammar entry can
  149. false-match Lower's own key. Lower re-notes its own VAR
  150. actuals, so starting from an empty ring is correct. *)
  151. nn := 0;
  152. i := 0;
  153. WHILE i <= HIGH(nVal) DO
  154. nVal[i][0] := CHR(0); nAddr[i][0] := CHR(0); INC(i)
  155. END
  156. END RestoreSession;
  157. (* ---------------- small string utilities ---------------- *)
  158. PROCEDURE Len (s: ARRAY OF CHAR): CARDINAL;
  159. VAR i : CARDINAL;
  160. BEGIN
  161. i := 0;
  162. WHILE (i <= HIGH(s)) AND (s[i] # CHR(0)) DO INC(i) END;
  163. RETURN i
  164. END Len;
  165. PROCEDURE Cpy (VAR d: ARRAY OF CHAR; s: ARRAY OF CHAR);
  166. VAR i : CARDINAL;
  167. BEGIN
  168. i := 0;
  169. WHILE (i <= HIGH(d)) AND (i <= HIGH(s)) AND (s[i] # CHR(0)) DO
  170. d[i] := s[i]; INC(i)
  171. END;
  172. IF i <= HIGH(d) THEN d[i] := CHR(0) END
  173. END Cpy;
  174. PROCEDURE App (VAR d: ARRAY OF CHAR; s: ARRAY OF CHAR);
  175. VAR i, j : CARDINAL;
  176. BEGIN
  177. i := 0;
  178. WHILE (i <= HIGH(d)) AND (d[i] # CHR(0)) DO INC(i) END;
  179. j := 0;
  180. WHILE (i <= HIGH(d)) AND (j <= HIGH(s)) AND (s[j] # CHR(0)) DO
  181. d[i] := s[j]; INC(i); INC(j)
  182. END;
  183. IF i <= HIGH(d) THEN d[i] := CHR(0) END
  184. END App;
  185. PROCEDURE BufApp (VAR buf: ARRAY OF CHAR; VAR used: CARDINAL;
  186. s: ARRAY OF CHAR; eol: BOOLEAN);
  187. VAR j : CARDINAL;
  188. BEGIN
  189. j := 0;
  190. WHILE (used < HIGH(buf)) AND (j <= HIGH(s)) AND (s[j] # CHR(0)) DO
  191. buf[used] := s[j]; INC(used); INC(j)
  192. END;
  193. IF eol AND (used < HIGH(buf)) THEN
  194. buf[used] := CHR(10); INC(used)
  195. END;
  196. IF used <= HIGH(buf) THEN buf[used] := CHR(0) END
  197. END BufApp;
  198. PROCEDURE WEmit (s: ARRAY OF CHAR; eol: BOOLEAN);
  199. (* Single output sink: the session buffer, or a nesting-depth
  200. buffer for nested functions (QBE rejects definitions inside
  201. functions, so nested bodies hoist until EndModule). *)
  202. VAR b, u, j : CARDINAL;
  203. BEGIN
  204. IF NOT opened OR noEmit THEN RETURN END;
  205. IF delayTop > 0 THEN
  206. b := delayTop - 1;
  207. u := delayUsed[b]; j := 0;
  208. WHILE (u < HIGH(delayBuf[b])) AND (j <= HIGH(s))
  209. AND (s[j] # CHR(0)) DO
  210. delayBuf[b][u] := s[j]; INC(u); INC(j)
  211. END;
  212. IF eol AND (u < HIGH(delayBuf[b])) THEN
  213. delayBuf[b][u] := CHR(10); INC(u)
  214. END;
  215. IF u <= HIGH(delayBuf[b]) THEN delayBuf[b][u] := CHR(0) END;
  216. delayUsed[b] := u;
  217. RETURN
  218. END;
  219. IF outSel = 0 THEN
  220. BufApp(sessBuf, sessUsed, s, eol);
  221. RETURN
  222. END;
  223. b := outSel - 1;
  224. IF b > HIGH(nestBufs) THEN b := HIGH(nestBufs) END;
  225. u := nestUsed[b];
  226. j := 0;
  227. WHILE (u < HIGH(nestBufs[b])) AND (j <= HIGH(s)) AND (s[j] # CHR(0)) DO
  228. nestBufs[b][u] := s[j]; INC(u); INC(j)
  229. END;
  230. IF eol AND (u < HIGH(nestBufs[b])) THEN
  231. nestBufs[b][u] := CHR(10); INC(u)
  232. END;
  233. IF u <= HIGH(nestBufs[b]) THEN nestBufs[b][u] := CHR(0) END;
  234. nestUsed[b] := u
  235. END WEmit;
  236. PROCEDURE W (s: ARRAY OF CHAR);
  237. BEGIN
  238. WEmit(s, FALSE)
  239. END W;
  240. PROCEDURE WL (s: ARRAY OF CHAR);
  241. BEGIN
  242. WEmit(s, TRUE)
  243. END WL;
  244. PROCEDURE SetNoEmit (b: BOOLEAN);
  245. BEGIN
  246. noEmit := b
  247. END SetNoEmit;
  248. PROCEDURE DelayBegin;
  249. (* Start buffering output (the RHS operand of a short-circuit AND/OR). *)
  250. BEGIN
  251. IF delayTop <= HIGH(delayBuf) THEN
  252. delayUsed[delayTop] := 0;
  253. delayBuf[delayTop][0] := CHR(0);
  254. INC(delayTop)
  255. END
  256. END DelayBegin;
  257. PROCEDURE DelayEnd;
  258. (* Stop buffering (restore the previous output sink). *)
  259. BEGIN
  260. IF delayTop > 0 THEN DEC(delayTop) END
  261. END DelayEnd;
  262. PROCEDURE DelayFlush;
  263. (* Emit the buffered RHS operand's code into the current sink.
  264. DelayEnd already popped delayTop, so the just-used buffer index is
  265. delayTop itself. *)
  266. VAR b: CARDINAL;
  267. BEGIN
  268. b := delayTop;
  269. IF b <= HIGH(delayBuf) THEN WEmit(delayBuf[b], FALSE) END
  270. END DelayFlush;
  271. PROCEDURE Slot4 (VAR s: QVal);
  272. BEGIN
  273. NewTemp(s);
  274. Revive;
  275. W(" "); W(s); W(" =l alloc4 4"); WL("")
  276. END Slot4;
  277. PROCEDURE StoreW (slot: ARRAY OF CHAR; val: ARRAY OF CHAR);
  278. BEGIN
  279. Revive;
  280. W(" storew "); W(val); W(", "); WL(slot)
  281. END StoreW;
  282. PROCEDURE LoadW (slot: ARRAY OF CHAR; VAR q: QVal);
  283. BEGIN
  284. NewTemp(q);
  285. Revive;
  286. W(" "); W(q); W(" =w loadw "); WL(slot)
  287. END LoadW;
  288. PROCEDURE SetModule (name: ARRAY OF CHAR);
  289. (* Names the module whose globals are being emitted (QBE symbols
  290. become "<mod>_<name>" so separate modules never collide). *)
  291. BEGIN
  292. Cpy(curModName, name)
  293. END SetModule;
  294. PROCEDURE SetOutName (name: ARRAY OF CHAR);
  295. (* Overrides the image file name written by EndModule (gen_ssa/<name>.ssa)
  296. without changing the symbol name EndModule mangles. Test-only hook for
  297. the two-phase Lower comparison. *)
  298. BEGIN
  299. Cpy(outName, name); hasOutName := TRUE
  300. END SetOutName;
  301. PROCEDURE SymRef (name: ARRAY OF CHAR; VAR ref: QVal);
  302. (* "$<mod>_<sym>" for module-level entries (avoids cross-module
  303. collisions); "$<name>" fallback otherwise. *)
  304. VAR g : SymTab.Name;
  305. BEGIN
  306. Cpy(ref, "$");
  307. IF SymTab.GlobalRef(name, g) THEN App(ref, g)
  308. ELSE App(ref, name)
  309. END
  310. END SymRef;
  311. PROCEDURE Wc (c: CHAR);
  312. (* Writes a single class character (w/d/l). *)
  313. VAR s : ARRAY [0 .. 1] OF CHAR;
  314. BEGIN
  315. s[0] := c; s[1] := CHR(0);
  316. W(s)
  317. END Wc;
  318. PROCEDURE AppNum (VAR d: ARRAY OF CHAR; v: CARDINAL);
  319. (* Appends v in decimal to d. *)
  320. VAR buf : ARRAY [0 .. 15] OF CHAR;
  321. n, i, L : CARDINAL;
  322. BEGIN
  323. n := 0;
  324. IF v = 0 THEN buf[0] := "0"; n := 1 END;
  325. WHILE (v > 0) AND (n <= HIGH(buf)) DO
  326. buf[n] := CHR(ORD("0") + v MOD 10); v := v DIV 10; INC(n)
  327. END;
  328. i := n;
  329. WHILE i > 0 DO
  330. DEC(i);
  331. L := Len(d);
  332. IF L < HIGH(d) THEN d[L] := buf[i]; d[L + 1] := CHR(0) END
  333. END
  334. END AppNum;
  335. PROCEDURE FwdDesignator (): INTEGER;
  336. (* Reserves a forward-global patch site and returns its id. *)
  337. BEGIN
  338. IF nFwd > HIGH(fwdSite) THEN RETURN 0 END;
  339. INC(nFwd);
  340. RETURN VAL(INTEGER, nFwd)
  341. END FwdDesignator;
  342. PROCEDURE FwdMangle (id: INTEGER; VAR q: QVal);
  343. BEGIN
  344. Cpy(q, "$fwd");
  345. AppNum(q, VAL(CARDINAL, id))
  346. END FwdMangle;
  347. PROCEDURE FwdAddrOper (id: INTEGER; VAR q: QVal);
  348. (* Stand-in address operand for forward site id. *)
  349. BEGIN
  350. FwdMangle(id, q)
  351. END FwdAddrOper;
  352. PROCEDURE FixLoadClass (ph: ARRAY OF CHAR; cls: CHAR);
  353. (* Re-class "<tmp> =<x> load<x> <ph>" -> "<tmp> =<cls> load<cls> <ph>".
  354. The default emit for an unresolved alias is `l` (pointer-like). *)
  355. VAR i, j, n: CARDINAL; found: BOOLEAN;
  356. BEGIN
  357. n := Len(ph);
  358. i := 0;
  359. WHILE i + n <= sessUsed DO
  360. (* "=c loadc " is 9 chars: '=', c, ' ', 'l','o','a','d', c, ' ' *)
  361. found := (i + 9 + n <= sessUsed);
  362. IF found THEN
  363. found := (sessBuf[i] = "=")
  364. AND (sessBuf[i+2] = " ")
  365. AND (sessBuf[i+3] = "l") AND (sessBuf[i+4] = "o")
  366. AND (sessBuf[i+5] = "a") AND (sessBuf[i+6] = "d")
  367. AND (sessBuf[i+8] = " ")
  368. AND (sessBuf[i+1] = sessBuf[i+7])
  369. END;
  370. IF found THEN
  371. j := 0; found := TRUE;
  372. WHILE (j < n) AND found DO
  373. IF sessBuf[i + 9 + j] # ph[j] THEN found := FALSE END; INC(j)
  374. END
  375. END;
  376. IF found THEN
  377. sessBuf[i + 1] := cls;
  378. sessBuf[i + 7] := cls
  379. END;
  380. INC(i)
  381. END
  382. END FixLoadClass;
  383. PROCEDURE FixStoreClass (ph: ARRAY OF CHAR; cls: CHAR);
  384. (* Re-class "store<x> <val>, <ph>" -> "store<cls> <val>, <ph>": blinks
  385. the opcode word that opens the line holding the operand <ph>. *)
  386. VAR i, j, n: CARDINAL; found: BOOLEAN;
  387. BEGIN
  388. n := Len(ph);
  389. i := 0;
  390. WHILE i + n <= sessUsed DO
  391. found := TRUE; j := 0;
  392. WHILE (j < n) AND found DO
  393. IF sessBuf[i + j] # ph[j] THEN found := FALSE END; INC(j)
  394. END;
  395. IF found THEN
  396. j := i;
  397. WHILE (j > 0) AND (sessBuf[j - 1] # CHR(10)) DO DEC(j) END;
  398. WHILE (j < i) AND ((sessBuf[j] = " ") OR (sessBuf[j] = CHR(9))) DO
  399. INC(j)
  400. END;
  401. IF (j + 6 <= i)
  402. AND (sessBuf[j] = "s") AND (sessBuf[j+1] = "t")
  403. AND (sessBuf[j+2] = "o") AND (sessBuf[j+3] = "r")
  404. AND (sessBuf[j+4] = "e") THEN
  405. sessBuf[j + 5] := cls
  406. END
  407. END;
  408. INC(i)
  409. END
  410. END FixStoreClass;
  411. PROCEDURE BufReplaceAll (pat, rep: ARRAY OF CHAR);
  412. (* Literal substring replacement over sessBuf (in place, memmove
  413. semantics for both growth and shrink). *)
  414. VAR i, j: CARDINAL; n, m, tail, k: CARDINAL; found: BOOLEAN;
  415. BEGIN
  416. n := Len(pat); m := Len(rep);
  417. IF (n = 0) OR (m = 0) THEN RETURN END;
  418. i := 0;
  419. WHILE i + n <= sessUsed DO
  420. found := TRUE; j := 0;
  421. WHILE (j < n) AND found DO
  422. IF sessBuf[i + j] # pat[j] THEN found := FALSE END; INC(j)
  423. END;
  424. IF NOT found THEN INC(i)
  425. ELSE
  426. IF m > n THEN
  427. tail := sessUsed;
  428. k := m - n;
  429. IF tail + k <= HIGH(sessBuf) THEN
  430. WHILE tail > i + n DO
  431. DEC(tail);
  432. sessBuf[tail + k] := sessBuf[tail]
  433. END;
  434. sessUsed := sessUsed + k
  435. END
  436. ELSIF m < n THEN
  437. tail := i + n;
  438. k := n - m;
  439. WHILE tail < sessUsed DO
  440. sessBuf[tail - k] := sessBuf[tail]; INC(tail)
  441. END;
  442. sessUsed := sessUsed - k
  443. END;
  444. j := 0;
  445. WHILE j < m DO sessBuf[i + j] := rep[j]; INC(j) END;
  446. i := i + m
  447. END
  448. END;
  449. IF sessUsed <= HIGH(sessBuf) THEN sessBuf[sessUsed] := CHR(0) END
  450. END BufReplaceAll;
  451. PROCEDURE FwdPatchAll;
  452. (* Rewrites each forward placeholder into the real designator, and
  453. re-classes the load that reads through it. Loads are emitted by the
  454. frontend as "<tmp> =<x> load<x> <ph>"; the default class is `w`, so
  455. for a `d`/`l` slot the opcode is rewritten to loadd/loadl. Stores
  456. are emitted as "store<x> <val>, <ph>"; the class letter of the store
  457. is rewritten in place. *)
  458. VAR k: INTEGER; ph, sym: QVal; c: CHAR;
  459. BEGIN
  460. k := 0;
  461. WHILE k < VAL(INTEGER, nFwd) DO
  462. IF fwdSite[k][0] # CHR(0) THEN
  463. FwdMangle(k + 1, ph);
  464. Cpy(sym, fwdSite[k]);
  465. IF fwdOper[k][0] = CHR(0) THEN c := "w" ELSE c := fwdOper[k][0] END;
  466. FixLoadClass(ph, c);
  467. FixStoreClass(ph, c);
  468. BufReplaceAll(ph, sym)
  469. END;
  470. INC(k)
  471. END;
  472. nFwd := 0
  473. END FwdPatchAll;
  474. (* ---------------- exported helpers ---------------- *)
  475. PROCEDURE CopyOp (s: ARRAY OF CHAR; VAR d: ARRAY OF CHAR);
  476. BEGIN
  477. Cpy(d, s)
  478. END CopyOp;
  479. PROCEDURE NoteAddr (val: ARRAY OF CHAR; addr: ARRAY OF CHAR);
  480. (* Records value-temp → address mapping for VAR actuals. Ring of
  481. 256: safe because SSA temps are never reused (stale entries can
  482. never false-match, only age out on pathological expressions). *)
  483. BEGIN
  484. Cpy(nVal[nn], val);
  485. Cpy(nAddr[nn], addr);
  486. nn := nn + 1;
  487. IF nn > HIGH(nVal) THEN nn := 0 END
  488. END NoteAddr;
  489. PROCEDURE AddrOfVal (val: ARRAY OF CHAR; VAR addr: QVal): BOOLEAN;
  490. VAR i : CARDINAL;
  491. BEGIN
  492. i := 0;
  493. WHILE i <= HIGH(nVal) DO
  494. IF SymTab.Equal(nVal[i], val) THEN
  495. Cpy(addr, nAddr[i]);
  496. RETURN TRUE
  497. END;
  498. INC(i)
  499. END;
  500. RETURN FALSE
  501. END AddrOfVal;
  502. PROCEDURE AddrOf (name: ARRAY OF CHAR; VAR q: QVal);
  503. VAR idx : INTEGER;
  504. levels : CARDINAL;
  505. BEGIN
  506. IF LocFindUp(name, levels, idx) THEN
  507. IF levels = 0 THEN
  508. IF locTag[idx] = 1 THEN
  509. Cpy(q, "$");
  510. App(q, locRep[idx])
  511. ELSE
  512. Cpy(q, locRep[idx])
  513. END
  514. ELSE
  515. UpAddrOf(idx, levels, q)
  516. END;
  517. RETURN
  518. END;
  519. SymRef(name, q)
  520. END AddrOf;
  521. PROCEDURE IntStr (v: INTEGER; VAR s: QVal);
  522. VAR neg : BOOLEAN;
  523. mag : CARDINAL;
  524. buf : ARRAY [0 .. 15] OF CHAR;
  525. n, i, L : CARDINAL;
  526. t : CHAR;
  527. BEGIN
  528. neg := v < 0;
  529. IF neg THEN mag := VAL(CARDINAL, -v) ELSE mag := VAL(CARDINAL, v) END;
  530. n := 0;
  531. IF mag = 0 THEN buf[0] := "0"; n := 1 END;
  532. WHILE (mag > 0) AND (n < HIGH(buf)) DO
  533. buf[n] := CHR(ORD("0") + mag MOD 10); mag := mag DIV 10; INC(n)
  534. END;
  535. i := 0;
  536. WHILE i < n DIV 2 DO
  537. t := buf[i]; buf[i] := buf[n - 1 - i]; buf[n - 1 - i] := t; INC(i)
  538. END;
  539. s[0] := CHR(0);
  540. IF neg THEN App(s, "-") END;
  541. i := 0;
  542. WHILE i < n DO
  543. L := Len(s);
  544. IF L < HIGH(s) THEN s[L] := buf[i]; s[L + 1] := CHR(0) END;
  545. INC(i)
  546. END
  547. END IntStr;
  548. PROCEDURE HexVal (ch: CHAR): INTEGER;
  549. BEGIN
  550. IF (ch >= "0") AND (ch <= "9") THEN RETURN ORD(ch) - ORD("0") END;
  551. IF (ch >= "A") AND (ch <= "F") THEN RETURN ORD(ch) - ORD("A") + 10 END;
  552. IF (ch >= "a") AND (ch <= "f") THEN RETURN ORD(ch) - ORD("a") + 10 END;
  553. RETURN 0
  554. END HexVal;
  555. PROCEDURE NormInt (s: ARRAY OF CHAR; VAR d: QVal);
  556. (* "0xFF" -> "255", plain decimals are copied through. *)
  557. VAR n, i : CARDINAL;
  558. v : INTEGER;
  559. BEGIN
  560. n := Len(s);
  561. IF (n > 2) AND (s[0] = "0")
  562. AND ((s[1] = "x") OR (s[1] = "X")) THEN
  563. v := 0; i := 2;
  564. WHILE i < n DO v := v * 16 + HexVal(s[i]); INC(i) END;
  565. IntStr(v, d)
  566. ELSE
  567. Cpy(d, s)
  568. END
  569. END NormInt;
  570. PROCEDURE NormLit (s: ARRAY OF CHAR; VAR d: QVal; VAR isCh: BOOLEAN);
  571. (* Full V3 integer literal: octal B/b, hex H/h and C/c suffixes plus
  572. the 0x/0X prefix; C marks a character constant. The suffix letter
  573. is excluded from the digit run (HexVal would read 'B' as 11).
  574. A 0x/0X prefix takes priority over any trailing B/C/H (C-style
  575. hex digits, so 0xB is 11, never an octal suffix). *)
  576. VAR n, i, hi, base : CARDINAL;
  577. v : INTEGER;
  578. ch : CHAR;
  579. BEGIN
  580. n := Len(s);
  581. isCh := FALSE;
  582. IF n = 0 THEN Cpy(d, "0"); RETURN END;
  583. i := 0;
  584. IF (n >= 3) AND (s[0] = "0") AND ((s[1] = "x") OR (s[1] = "X")) THEN
  585. base := 16; hi := n; i := 2
  586. ELSE
  587. ch := s[n - 1];
  588. IF (ch = "B") OR (ch = "b") THEN
  589. base := 8; hi := n - 1
  590. ELSIF (ch = "H") OR (ch = "h") THEN
  591. base := 16; hi := n - 1
  592. ELSIF (ch = "C") OR (ch = "c") THEN
  593. base := 16; hi := n - 1; isCh := TRUE
  594. ELSE
  595. base := 10; hi := n
  596. END
  597. END;
  598. v := 0;
  599. WHILE i < hi DO
  600. v := v * VAL(INTEGER, base) + HexVal(s[i]); INC(i)
  601. END;
  602. IntStr(v, d)
  603. END NormLit;
  604. PROCEDURE NormReal (s: ARRAY OF CHAR; VAR d: QVal);
  605. (* "3.14" -> "d_3.14" (QBE double immediates carry a d_ prefix). *)
  606. BEGIN
  607. Cpy(d, "d_");
  608. App(d, s)
  609. END NormReal;
  610. PROCEDURE CharVal (s: ARRAY OF CHAR): INTEGER;
  611. BEGIN
  612. IF Len(s) >= 2 THEN RETURN ORD(s[1]) END;
  613. RETURN 0
  614. END CharVal;
  615. PROCEDURE NegFold (a: ARRAY OF CHAR; VAR q: QVal);
  616. (* Safe for a and q being the same variable. *)
  617. VAR i, j, L : CARDINAL;
  618. tmp : QVal;
  619. BEGIN
  620. Cpy(tmp, a);
  621. IF Len(tmp) = 0 THEN Cpy(q, "0"); RETURN END;
  622. IF tmp[0] = "-" THEN
  623. i := 1; j := 0;
  624. WHILE (tmp[i] # CHR(0)) AND (j < HIGH(q)) DO
  625. q[j] := tmp[i]; INC(i); INC(j)
  626. END;
  627. IF j <= HIGH(q) THEN q[j] := CHR(0) END
  628. ELSIF (tmp[0] = "d") AND (Len(tmp) > 1) AND (tmp[1] = "_") THEN
  629. Cpy(q, "d_-");
  630. i := 2;
  631. WHILE tmp[i] # CHR(0) DO
  632. L := Len(q);
  633. IF L < HIGH(q) THEN q[L] := tmp[i]; q[L + 1] := CHR(0) END;
  634. INC(i)
  635. END
  636. ELSE
  637. Cpy(q, "-");
  638. App(q, tmp)
  639. END
  640. END NegFold;
  641. PROCEDURE IsImm (s: ARRAY OF CHAR): BOOLEAN;
  642. BEGIN
  643. IF Len(s) = 0 THEN RETURN FALSE END;
  644. RETURN ((s[0] >= "0") AND (s[0] <= "9")) OR (s[0] = "-")
  645. OR (s[0] = "d")
  646. END IsImm;
  647. PROCEDURE ParseInt (s: ARRAY OF CHAR; VAR v: INTEGER): BOOLEAN;
  648. VAR i, nd, d: CARDINAL;
  649. neg: BOOLEAN;
  650. BEGIN
  651. v := 0; i := 0; neg := FALSE; nd := 0;
  652. IF (i <= HIGH(s)) AND (s[i] = "-") THEN neg := TRUE; INC(i) END;
  653. IF (i > HIGH(s)) OR (s[i] = CHR(0)) THEN RETURN FALSE END;
  654. WHILE (i <= HIGH(s)) AND (s[i] # CHR(0)) DO
  655. d := ORD(s[i]);
  656. IF (d < ORD("0")) OR (d > ORD("9")) THEN RETURN FALSE END;
  657. v := v * 10 + VAL(INTEGER, d - ORD("0"));
  658. INC(i); INC(nd)
  659. END;
  660. IF nd = 0 THEN RETURN FALSE END;
  661. IF neg THEN v := -v END;
  662. RETURN TRUE
  663. END ParseInt;
  664. PROCEDURE Fold2 (kind: INTEGER; a, b: ARRAY OF CHAR; VAR r: QVal): BOOLEAN;
  665. VAR x, y, z: INTEGER;
  666. BEGIN
  667. IF NOT ParseInt(a, x) THEN RETURN FALSE END;
  668. IF NOT ParseInt(b, y) THEN RETURN FALSE END;
  669. IF kind = 0 THEN z := x + y
  670. ELSIF kind = 1 THEN z := x - y
  671. ELSIF kind = 2 THEN z := x * y
  672. ELSIF kind = 3 THEN
  673. IF y = 0 THEN RETURN FALSE END;
  674. z := x DIV y
  675. ELSE
  676. IF y = 0 THEN RETURN FALSE END;
  677. z := x MOD y
  678. END;
  679. IntStr(z, r);
  680. RETURN TRUE
  681. END Fold2;
  682. (* ---------------- module / data section ---------------- *)
  683. PROCEDURE OpenModule (name: ARRAY OF CHAR);
  684. VAR i : CARDINAL;
  685. BEGIN
  686. opened := TRUE;
  687. inBody := FALSE;
  688. dead := FALSE;
  689. hasOutName := FALSE;
  690. nTemp := 0; nLab := 0; loopTop := 0; nR := 0; nStr := 0;
  691. withTop := 0;
  692. noEmit := FALSE; inFunc := FALSE; useStack := FALSE;
  693. ctorN := 0; ctorTop := 0;
  694. nLoc := 0; nPar := 0; nArg := 0; nn := 0; callDepth := 0;
  695. recvArmed := FALSE;
  696. funcDepth := 0; scopeTop := 0; scopeBase[0] := 0;
  697. nInit := 0;
  698. outSel := 0;
  699. sessUsed := 0; sessBuf[0] := CHR(0);
  700. delayTop := 0;
  701. ustrN := 0; ustrUsed := 0;
  702. i := 0;
  703. WHILE i <= HIGH(nestBufs) DO
  704. nestBufs[i][0] := CHR(0); nestUsed[i] := 0; INC(i)
  705. END;
  706. WL("# QBE IR generated by the V3 step-1 backend");
  707. WL("")
  708. END OpenModule;
  709. PROCEDURE DataLine (name: ARRAY OF CHAR; isReal: BOOLEAN;
  710. init: ARRAY OF CHAR);
  711. BEGIN
  712. IF NOT opened THEN RETURN END;
  713. W("data $"); W(name);
  714. IF isReal THEN W(" = { d ") ELSE W(" = { w ") END;
  715. W(init);
  716. WL(" }")
  717. END DataLine;
  718. PROCEDURE DataLineL (name: ARRAY OF CHAR; init: ARRAY OF CHAR);
  719. BEGIN
  720. IF NOT opened THEN RETURN END;
  721. W("data $"); W(name);
  722. W(" = { l ");
  723. W(init);
  724. WL(" }")
  725. END DataLineL;
  726. PROCEDURE DeclLocal (name: ARRAY OF CHAR; t: INTEGER);
  727. (* Function-local variable: stack slot + static init, recorded. *)
  728. VAR slot : QVal;
  729. BEGIN
  730. AllocLocal(t, slot);
  731. LocAdd(name, 0, slot, ResClass(t), t)
  732. END DeclLocal;
  733. PROCEDURE DeclVar (name: ARRAY OF CHAR; t: INTEGER);
  734. (* Module-level variable: emitted as data "$<mod>_<name>". *)
  735. VAR g : QVal;
  736. BEGIN
  737. IF inFunc THEN DeclLocal(name, t); RETURN END;
  738. Cpy(g, curModName); App(g, "_"); App(g, name);
  739. IF SymTab.ClassOf(t) = SymTab.ClReal THEN DataLine(g, TRUE, "0")
  740. ELSIF SymTab.ClassOf(t) = SymTab.ClArray THEN DeclArr(g, t)
  741. ELSIF SymTab.ClassOf(t) = SymTab.ClSet THEN DeclSet(g, t)
  742. ELSIF (SymTab.ClassOf(t) = SymTab.ClRecord)
  743. OR (SymTab.ClassOf(t) = SymTab.ClClass) THEN
  744. DeclRec(g, t)
  745. ELSIF (SymTab.ClassOf(t) = SymTab.ClPtr)
  746. OR (SymTab.ClassOf(t) = SymTab.ClLong)
  747. OR (SymTab.ClassOf(t) = SymTab.ClProc) THEN DataLineL(g, "0")
  748. ELSE DataLine(g, FALSE, "0")
  749. END
  750. END DeclVar;
  751. PROCEDURE IsZeroReal (val: ARRAY OF CHAR): BOOLEAN;
  752. (* TRUE when the "d_..." literal denotes zero (checked textually). *)
  753. VAR i : CARDINAL;
  754. seen : BOOLEAN;
  755. ch : CHAR;
  756. BEGIN
  757. i := 0;
  758. IF (Len(val) > 1) AND (val[0] = "d") AND (val[1] = "_") THEN i := 2 END;
  759. IF (Len(val) > i) AND (val[i] = "-") THEN INC(i) END;
  760. seen := FALSE;
  761. WHILE val[i] # CHR(0) DO
  762. ch := val[i];
  763. IF (ch >= "0") AND (ch <= "9") THEN
  764. seen := TRUE;
  765. IF ch # "0" THEN RETURN FALSE END
  766. ELSIF (ch # ".") AND (ch # "E") AND (ch # "e")
  767. AND (ch # "+") AND (ch # "-") THEN
  768. RETURN FALSE
  769. END;
  770. INC(i)
  771. END;
  772. RETURN seen
  773. END IsZeroReal;
  774. PROCEDURE DeclConst (name: ARRAY OF CHAR; val: ARRAY OF CHAR; t: INTEGER);
  775. VAR cls : INTEGER;
  776. mang : QVal;
  777. BEGIN
  778. cls := SymTab.ClassOf(t);
  779. IF inFunc THEN
  780. Cpy(mang, funcName); App(mang, "_"); App(mang, name);
  781. LocAdd(name, 1, mang, ResClass(t), t)
  782. ELSE
  783. Cpy(mang, curModName); App(mang, "_"); App(mang, name)
  784. END;
  785. IF (cls = SymTab.ClStr) OR NOT IsImm(val) THEN
  786. DataLine(mang, FALSE, "0"); RETURN
  787. END;
  788. IF cls = SymTab.ClLong THEN
  789. DataLineL(mang, val)
  790. ELSIF cls = SymTab.ClReal THEN
  791. (* This qbe accepts only integer/zero literals in "data":
  792. non-zero REALs flush at BeginBody. *)
  793. DataLine(mang, TRUE, "0");
  794. IF NOT IsZeroReal(val) AND (nR <= HIGH(rNames)) THEN
  795. Cpy(rNames[nR], mang); Cpy(rVals[nR], val); INC(nR)
  796. END
  797. ELSE DataLine(mang, FALSE, val)
  798. END
  799. END DeclConst;
  800. (* ---------------- function body ---------------- *)
  801. PROCEDURE BeginInit (mod: ARRAY OF CHAR);
  802. (* Module BEGIN body as `<mod>_init`; main calls it at startup. *)
  803. VAR sym : QVal;
  804. BEGIN
  805. IF NOT opened THEN RETURN END;
  806. Cpy(sym, mod);
  807. App(sym, "_init");
  808. WL("");
  809. W("export function w $"); W(sym); WL("() {");
  810. WL("@start");
  811. IF nInit <= HIGH(initNames) THEN
  812. Cpy(initNames[nInit], sym); INC(nInit)
  813. END
  814. END BeginInit;
  815. PROCEDURE EndInit;
  816. BEGIN
  817. Revive;
  818. WL(" ret 0");
  819. WL("}")
  820. END EndInit;
  821. PROCEDURE BeginBody;
  822. VAR i : CARDINAL;
  823. t : QVal;
  824. BEGIN
  825. IF NOT opened OR inBody THEN RETURN END;
  826. WL("");
  827. WL("export function w $main() {");
  828. WL("@start");
  829. inBody := TRUE;
  830. i := 0;
  831. WHILE i < nR DO
  832. NewTemp(t);
  833. W(" "); W(t); W(" =d copy "); WL(rVals[i]);
  834. StoreVar(rNames[i], t, TRUE);
  835. INC(i)
  836. END;
  837. i := 0;
  838. WHILE i < nInit DO
  839. W(" call $"); W(initNames[i]); WL("()");
  840. INC(i)
  841. END
  842. END BeginBody;
  843. PROCEDURE EndModule (name: ARRAY OF CHAR);
  844. VAR i : CARDINAL;
  845. fname : ARRAY [0 .. 127] OF CHAR;
  846. ref : QVal;
  847. BEGIN
  848. IF NOT opened THEN RETURN END;
  849. (* Per-unit Lower emission: the grammar's own unit-end EndModule must
  850. not flush/close/write while the legacy emit is suppressed. The
  851. real session image is written by Lower.EndSession. *)
  852. IF noEmit THEN RETURN END;
  853. IF NOT inBody THEN BeginBody END;
  854. IF SymTab.Lookup("ExitCode")
  855. AND (SymTab.SymKind("ExitCode") = SymTab.KindVar)
  856. AND SymTab.IsIntFamily(SymTab.SymType("ExitCode")) THEN
  857. Cpy(ref, "$"); App(ref, name); App(ref, "_ExitCode");
  858. W(" %ec =w loadw "); WL(ref);
  859. WL(" ret %ec")
  860. ELSE
  861. WL(" ret 0")
  862. END;
  863. WL("}");
  864. (* hoisted nested functions, then deferred data *)
  865. i := 0;
  866. WHILE i <= HIGH(nestBufs) DO
  867. IF nestUsed[i] > 0 THEN
  868. BufApp(sessBuf, sessUsed, nestBufs[i], FALSE)
  869. END;
  870. INC(i)
  871. END;
  872. FlushStrings;
  873. FlushUStrings;
  874. FlushCtors;
  875. EmitVTables;
  876. FwdPatchAll;
  877. (* the image is named after the program module *)
  878. fname[0] := CHR(0);
  879. App(fname, "gen_ssa/");
  880. IF hasOutName THEN App(fname, outName) ELSE App(fname, name) END;
  881. App(fname, ".ssa");
  882. FileIO.Open(out, fname, TRUE);
  883. IF FileIO.Okay THEN
  884. (* Flush exactly sessUsed bytes. FileIO.WriteString scans for a
  885. NUL and misbehaves for multi-megabyte buffers, so write the
  886. image byte by byte up to sessUsed. *)
  887. i := 0;
  888. WHILE i < sessUsed DO
  889. FileIO.Write(out, sessBuf[i]); INC(i)
  890. END
  891. END;
  892. FileIO.Close(out);
  893. opened := FALSE
  894. END EndModule;
  895. (* ---------------- functions and calls (step 4.1) ---------------- *)
  896. (* Module-level procedures only (nested → NoEmit until 4.2).
  897. Value params arrive as SSA values and are copied to slots;
  898. composite value params arrive as addresses and are copied;
  899. VAR params stay addresses. Locals live in stack slots. *)
  900. PROCEDURE LocFind (name: ARRAY OF CHAR): INTEGER;
  901. VAR i : CARDINAL;
  902. BEGIN
  903. i := 0;
  904. WHILE i < nLoc DO
  905. IF SymTab.Equal(locNames[i], name) THEN RETURN VAL(INTEGER, i) END;
  906. INC(i)
  907. END;
  908. RETURN -1
  909. END LocFind;
  910. PROCEDURE ScopeBegin;
  911. (* Pushes a procedure scope (always balanced, even under NoEmit). *)
  912. BEGIN
  913. IF scopeTop > HIGH(scopeLink) THEN RETURN END;
  914. scopeBase[scopeTop] := nLoc;
  915. INC(scopeTop);
  916. scopeBase[scopeTop] := nLoc
  917. END ScopeBegin;
  918. PROCEDURE ScopeEnd;
  919. BEGIN
  920. IF scopeTop > 0 THEN
  921. DEC(scopeTop);
  922. nLoc := scopeBase[scopeTop]
  923. END
  924. END ScopeEnd;
  925. PROCEDURE LocFindUp (name: ARRAY OF CHAR; VAR levels: CARDINAL;
  926. VAR flat: INTEGER): BOOLEAN;
  927. (* Innermost procedure scope holding name; levels = scopes crossed,
  928. flat = table index. FALSE when purely global. *)
  929. VAR s, top, lo, hi, j : CARDINAL;
  930. BEGIN
  931. levels := 0; flat := -1;
  932. IF scopeTop = 0 THEN RETURN FALSE END;
  933. top := scopeTop - 1;
  934. s := top;
  935. LOOP
  936. lo := scopeBase[s];
  937. IF s = top THEN hi := nLoc ELSE hi := scopeBase[s + 1] END;
  938. j := lo;
  939. WHILE j < hi DO
  940. IF SymTab.Equal(locNames[j], name) THEN
  941. levels := top - s;
  942. flat := VAL(INTEGER, j);
  943. RETURN TRUE
  944. END;
  945. INC(j)
  946. END;
  947. IF s = 0 THEN RETURN FALSE END;
  948. DEC(s)
  949. END
  950. END LocFindUp;
  951. PROCEDURE LocAdd (name: ARRAY OF CHAR; tag: INTEGER; rep: ARRAY OF CHAR;
  952. cls: CHAR; typ: INTEGER);
  953. VAR idx : CARDINAL;
  954. off : QVal;
  955. lk : QVal;
  956. BEGIN
  957. IF (scopeTop = 0) OR (nLoc > HIGH(locNames)) THEN RETURN END;
  958. idx := nLoc - scopeBase[scopeTop - 1];
  959. IF idx > 63 THEN RETURN END;
  960. Cpy(locNames[nLoc], name);
  961. locTag[nLoc] := tag;
  962. Cpy(locRep[nLoc], rep);
  963. locCls[nLoc] := cls;
  964. locTyp[nLoc] := typ;
  965. INC(nLoc);
  966. IF tag = 1 THEN RETURN END;
  967. Cpy(lk, scopeLink[scopeTop - 1]);
  968. IntStr(VAL(INTEGER, 8 * (idx + 1)), off);
  969. NewTemp(lk);
  970. Op3L("add", lk, scopeLink[scopeTop - 1], off);
  971. Revive;
  972. W(" storel "); W(rep); W(", "); WL(lk)
  973. END LocAdd;
  974. PROCEDURE LocFull (): BOOLEAN;
  975. (* TRUE past 64 locals in the current scope (→ 233). *)
  976. BEGIN
  977. IF scopeTop = 0 THEN RETURN FALSE END;
  978. RETURN nLoc - scopeBase[scopeTop - 1] > 63
  979. END LocFull;
  980. PROCEDURE UpAddr (levels: CARDINAL; rel: CARDINAL; VAR q: QVal);
  981. (* Address of an up-level local: walks the static chain, then
  982. loads the link cell (slot address or actual address). *)
  983. VAR cur, t, off : QVal;
  984. L : CARDINAL;
  985. BEGIN
  986. Cpy(cur, scopeLink[scopeTop - 1]);
  987. L := levels;
  988. WHILE L > 0 DO
  989. NewTemp(t);
  990. Revive;
  991. W(" "); W(t); W(" =l loadl "); WL(cur);
  992. Cpy(cur, t);
  993. DEC(L)
  994. END;
  995. IntStr(VAL(INTEGER, 8 * (rel + 1)), off);
  996. NewTemp(t);
  997. Op3L("add", t, cur, off);
  998. NewTemp(q);
  999. Revive;
  1000. W(" "); W(q); W(" =l loadl "); WL(t)
  1001. END UpAddr;
  1002. PROCEDURE ResClass (t: INTEGER): CHAR;
  1003. VAR cls : INTEGER;
  1004. BEGIN
  1005. cls := SymTab.ClassOf(t);
  1006. IF cls = SymTab.ClReal THEN RETURN "d" END;
  1007. IF (cls = SymTab.ClArray) OR (cls = SymTab.ClSet)
  1008. OR (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass)
  1009. OR (cls = SymTab.ClPtr) OR (cls = SymTab.ClLong)
  1010. OR (cls = SymTab.ClProc) THEN
  1011. RETURN "l"
  1012. END;
  1013. RETURN "w"
  1014. END ResClass;
  1015. PROCEDURE Mangled (pname: ARRAY OF CHAR; uid: CARDINAL; VAR q: QVal);
  1016. (* External procedures bind to their C link name; everything else
  1017. mangles to <name>_<uid> (deterministic, collision-free). *)
  1018. VAR link, base : SymTab.Name;
  1019. BEGIN
  1020. IF SymTab.ProcLink(pname, link) THEN
  1021. Cpy(q, link);
  1022. RETURN
  1023. END;
  1024. IF NOT SymTab.SymBase(pname, base) THEN Cpy(base, pname) END;
  1025. Cpy(q, base);
  1026. App(q, "_");
  1027. AppNum(q, uid)
  1028. END Mangled;
  1029. PROCEDURE AllocLocal (t: INTEGER; VAR slot: QVal);
  1030. (* Stack slot sized for t's inline footprint; arrays/records get
  1031. full objects + static init. Scalar/pointer/set slots are
  1032. zero-initialized: QBE rejects reads of never-stored slots, and
  1033. the frontend emits a dead load for assignment targets (Test2
  1034. heritage, harmless for zero-initialized globals). Locals match
  1035. the globals' zero-init convention. *)
  1036. VAR cls : INTEGER;
  1037. nb : QVal;
  1038. BEGIN
  1039. cls := SymTab.ClassOf(t);
  1040. NewTemp(slot);
  1041. Revive;
  1042. W(" "); W(slot);
  1043. IF (cls = SymTab.ClArray) THEN
  1044. IntStr(VAL(INTEGER, HeapSize(t)), nb);
  1045. W(" =l alloc8 "); WL(nb);
  1046. InitStack(slot, t)
  1047. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  1048. IntStr(VAL(INTEGER, SymTab.TypeSize(t)), nb);
  1049. W(" =l alloc8 "); WL(nb);
  1050. InitStack(slot, t)
  1051. ELSIF cls = SymTab.ClSet THEN
  1052. IntStr(VAL(INTEGER, SymTab.SetWords(t) * 4), nb);
  1053. W(" =l alloc8 "); WL(nb);
  1054. SetZero(slot, SymTab.SetWords(t))
  1055. ELSIF cls = SymTab.ClReal THEN
  1056. W(" =l alloc8 8"); WL("");
  1057. W(" stored d_0.0, "); WL(slot)
  1058. ELSIF (cls = SymTab.ClPtr) OR (cls = SymTab.ClLong)
  1059. OR (cls = SymTab.ClProc) THEN
  1060. W(" =l alloc8 8"); WL("");
  1061. W(" storel 0, "); WL(slot)
  1062. ELSE
  1063. W(" =l alloc4 4"); WL("");
  1064. W(" storew 0, "); WL(slot)
  1065. END
  1066. END AllocLocal;
  1067. PROCEDURE BeginFunc (mangled: ARRAY OF CHAR);
  1068. (* Opens function context and scope; the header (with a leading
  1069. static-link param) buffers until EndFuncHeader. FORWARD/DefUnit
  1070. paths discard via AbortFunc. *)
  1071. BEGIN
  1072. ScopeBegin;
  1073. INC(funcDepth);
  1074. inFunc := TRUE;
  1075. nPar := 0;
  1076. funcRes := "w";
  1077. Cpy(funcName, mangled);
  1078. hbuf[0] := CHR(0);
  1079. NewTemp(slTmp);
  1080. HApp("l ");
  1081. HApp(slTmp);
  1082. hdrComma := TRUE;
  1083. IF funcDepth > 1 THEN
  1084. outSel := funcDepth - 1;
  1085. IF outSel > HIGH(nestBufs) + 1 THEN
  1086. outSel := HIGH(nestBufs) + 1
  1087. END
  1088. ELSE
  1089. outSel := 0
  1090. END
  1091. END BeginFunc;
  1092. PROCEDURE SetFuncRes (t: INTEGER);
  1093. BEGIN
  1094. funcRes := ResClass(t)
  1095. END SetFuncRes;
  1096. PROCEDURE HApp (s: ARRAY OF CHAR);
  1097. (* Appends to the header buffer (bounded: FormalParams cap it). *)
  1098. VAR i, j : CARDINAL;
  1099. BEGIN
  1100. i := 0;
  1101. WHILE (i <= HIGH(hbuf)) AND (hbuf[i] # CHR(0)) DO INC(i) END;
  1102. j := 0;
  1103. WHILE (i < HIGH(hbuf)) AND (j <= HIGH(s)) AND (s[j] # CHR(0)) DO
  1104. hbuf[i] := s[j]; INC(i); INC(j)
  1105. END;
  1106. IF i <= HIGH(hbuf) THEN hbuf[i] := CHR(0) END
  1107. END HApp;
  1108. PROCEDURE FuncParam (name: ARRAY OF CHAR; isVar: BOOLEAN;
  1109. t: INTEGER): BOOLEAN;
  1110. VAR c : CHAR;
  1111. tmp : QVal;
  1112. buf : ARRAY [0 .. 71] OF CHAR;
  1113. BEGIN
  1114. IF nPar > HIGH(parCls) THEN RETURN FALSE END;
  1115. IF isVar THEN c := "l" ELSE c := ResClass(t) END;
  1116. parCls[nPar] := c;
  1117. NewTemp(tmp);
  1118. Cpy(parTmp[nPar], tmp);
  1119. Cpy(parNam[nPar], name);
  1120. parVar[nPar] := isVar;
  1121. parTyp[nPar] := t;
  1122. IF hdrComma THEN HApp(", ") END;
  1123. hdrComma := TRUE;
  1124. buf[0] := c; buf[1] := " "; buf[2] := CHR(0);
  1125. App(buf, tmp);
  1126. HApp(buf);
  1127. INC(nPar);
  1128. RETURN TRUE
  1129. END FuncParam;
  1130. PROCEDURE EndFuncHeader;
  1131. (* Flushes the buffered header + body label, allocates the link
  1132. record ([0] = parent link), then emits entry copies. *)
  1133. VAR i : CARDINAL;
  1134. slot, nb, lk : QVal;
  1135. cls : INTEGER;
  1136. BEGIN
  1137. W("export function "); Wc(funcRes); W(" $"); W(funcName); W("(");
  1138. W(hbuf);
  1139. WL(") {");
  1140. WL("@start");
  1141. inBody := TRUE;
  1142. NewTemp(lk);
  1143. Cpy(scopeLink[scopeTop - 1], lk);
  1144. Revive;
  1145. W(" "); W(lk); W(" =l alloc8 520"); WL("");
  1146. W(" storel "); W(slTmp); W(", "); WL(lk);
  1147. i := 0;
  1148. WHILE i < nPar DO
  1149. cls := SymTab.ClassOf(parTyp[i]);
  1150. IF parVar[i] THEN
  1151. LocAdd(parNam[i], 2, parTmp[i], parCls[i], parTyp[i])
  1152. ELSE
  1153. AllocLocal(parTyp[i], slot);
  1154. LocAdd(parNam[i], 0, slot, parCls[i], parTyp[i]);
  1155. IF cls = SymTab.ClArray THEN
  1156. CopyArray(slot, parTmp[i], parTyp[i])
  1157. ELSIF cls = SymTab.ClSet THEN
  1158. CopySet(slot, parTmp[i],
  1159. SymTab.SetWords(parTyp[i]), SymTab.SetWords(parTyp[i]))
  1160. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  1161. CopyRecord(slot, parTmp[i], parTyp[i])
  1162. ELSIF cls = SymTab.ClReal THEN
  1163. Revive;
  1164. W(" stored "); W(parTmp[i]); W(", "); WL(slot)
  1165. ELSIF (cls = SymTab.ClPtr) OR (cls = SymTab.ClProc)
  1166. OR (cls = SymTab.ClLong) THEN
  1167. Revive;
  1168. W(" storel "); W(parTmp[i]); W(", "); WL(slot)
  1169. ELSE
  1170. Revive;
  1171. W(" storew "); W(parTmp[i]); W(", "); WL(slot)
  1172. END
  1173. END;
  1174. INC(i)
  1175. END
  1176. END EndFuncHeader;
  1177. PROCEDURE AbortFunc;
  1178. (* Discards function context without emitting (DefUnit headings,
  1179. FORWARD, methods): pops the scope, restores the depth. *)
  1180. BEGIN
  1181. ScopeEnd;
  1182. IF funcDepth > 0 THEN DEC(funcDepth) END;
  1183. inFunc := funcDepth > 0;
  1184. inBody := FALSE;
  1185. nPar := 0;
  1186. IF funcDepth > 1 THEN outSel := funcDepth - 1
  1187. ELSE outSel := 0
  1188. END
  1189. END AbortFunc;
  1190. PROCEDURE EndFunc (resT: INTEGER);
  1191. VAR rc : CHAR;
  1192. BEGIN
  1193. rc := ResClass(resT);
  1194. Revive;
  1195. IF rc = "d" THEN WL(" ret d_0.0")
  1196. ELSIF rc = "l" THEN WL(" ret 0")
  1197. ELSE WL(" ret 0")
  1198. END;
  1199. WL("}");
  1200. ScopeEnd;
  1201. IF funcDepth > 0 THEN DEC(funcDepth) END;
  1202. inFunc := funcDepth > 0;
  1203. inBody := FALSE;
  1204. IF funcDepth > 1 THEN outSel := funcDepth - 1
  1205. ELSE outSel := 0
  1206. END
  1207. END EndFunc;
  1208. PROCEDURE EmitRet (q: ARRAY OF CHAR; hasVal: BOOLEAN);
  1209. BEGIN
  1210. Revive;
  1211. IF hasVal THEN W(" ret "); WL(q)
  1212. ELSE WL(" ret 0")
  1213. END;
  1214. dead := TRUE
  1215. END EmitRet;
  1216. PROCEDURE ProcAddr (mangled: ARRAY OF CHAR; VAR q: QVal);
  1217. BEGIN
  1218. Cpy(q, "$");
  1219. App(q, mangled)
  1220. END ProcAddr;
  1221. PROCEDURE CallBeginInd (callee: ARRAY OF CHAR; resT: INTEGER;
  1222. isExt: BOOLEAN);
  1223. (* Indirect call through the code pointer in `callee`. *)
  1224. BEGIN
  1225. IF callDepth > HIGH(stkName) THEN RETURN END;
  1226. Cpy(stkName[callDepth], callee);
  1227. stkRes[callDepth] := ResClass(resT);
  1228. stkExt[callDepth] := isExt;
  1229. stkInd[callDepth] := TRUE;
  1230. Cpy(stkLink[callDepth], "0");
  1231. stkArg[callDepth][0] := CHR(0);
  1232. stkN[callDepth] := 0;
  1233. INC(callDepth);
  1234. IF recvArmed THEN
  1235. IF NOT CallArg(recvQ, "l") THEN
  1236. END;
  1237. recvArmed := FALSE
  1238. END
  1239. END CallBeginInd;
  1240. PROCEDURE VirtCallBegin (obj: ARRAY OF CHAR; slot: INTEGER; resT: INTEGER);
  1241. (* Virtual dispatch: load obj's vtable pointer, fetch slot `slot`,
  1242. and begin an indirect call through it. The receiver (obj) must be
  1243. armed (ArmRecv). *)
  1244. VAR vt, off, ea, fp: QVal;
  1245. BEGIN
  1246. NewTemp(vt);
  1247. W(" "); W(vt); W(" =l loadl "); WL(obj);
  1248. IntStr(slot * 8, off);
  1249. NewTemp(ea);
  1250. Op3L("add", ea, vt, off);
  1251. NewTemp(fp);
  1252. W(" "); W(fp); W(" =l loadl "); WL(ea);
  1253. CallBeginInd(fp, resT, FALSE)
  1254. END VirtCallBegin;
  1255. PROCEDURE ArmRecv (q: ARRAY OF CHAR);
  1256. (* Arms the receiver for the next class-method call: CallBegin passes
  1257. it as the hidden first argument. *)
  1258. BEGIN
  1259. Cpy(recvQ, q);
  1260. recvArmed := TRUE
  1261. END ArmRecv;
  1262. PROCEDURE CallBegin (mangled: ARRAY OF CHAR; resT: INTEGER;
  1263. fdep: CARDINAL; isExt: BOOLEAN);
  1264. (* Pushes a call level; the static link is resolved now (caller
  1265. context cannot change mid-call): module callers pass 0, others
  1266. walk the chain (inFuncDepth - fdep) from their link record.
  1267. External callees take no static link. *)
  1268. VAR walks : INTEGER;
  1269. cur, t : QVal;
  1270. BEGIN
  1271. IF callDepth > HIGH(stkName) THEN RETURN END;
  1272. Cpy(stkName[callDepth], mangled);
  1273. stkRes[callDepth] := ResClass(resT);
  1274. stkExt[callDepth] := isExt;
  1275. stkInd[callDepth] := FALSE;
  1276. stkArg[callDepth][0] := CHR(0);
  1277. stkN[callDepth] := 0;
  1278. IF isExt THEN
  1279. Cpy(stkLink[callDepth], "0")
  1280. ELSIF funcDepth = 0 THEN
  1281. Cpy(stkLink[callDepth], "0")
  1282. ELSE
  1283. walks := VAL(INTEGER, funcDepth) - VAL(INTEGER, fdep);
  1284. Cpy(cur, scopeLink[scopeTop - 1]);
  1285. WHILE walks > 0 DO
  1286. NewTemp(t);
  1287. Revive;
  1288. W(" "); W(t); W(" =l loadl "); WL(cur);
  1289. Cpy(cur, t);
  1290. DEC(walks)
  1291. END;
  1292. Cpy(stkLink[callDepth], cur)
  1293. END;
  1294. INC(callDepth);
  1295. IF recvArmed THEN
  1296. (* class method: the receiver is the hidden first argument *)
  1297. IF NOT CallArg(recvQ, "l") THEN
  1298. END;
  1299. recvArmed := FALSE
  1300. END
  1301. END CallBegin;
  1302. PROCEDURE CallArg (q: ARRAY OF CHAR; cls: CHAR): BOOLEAN;
  1303. (* Appends "c operand, " to the current level. FALSE when full. *)
  1304. VAR need, have, i, j, d : CARDINAL;
  1305. BEGIN
  1306. d := callDepth - 1;
  1307. IF callDepth = 0 THEN RETURN FALSE END;
  1308. IF d > HIGH(stkArg) THEN d := HIGH(stkArg) END;
  1309. need := Len(q) + 5;
  1310. have := Len(stkArg[d]);
  1311. IF (stkN[d] >= 64) OR (have + need > HIGH(stkArg[d])) THEN
  1312. RETURN FALSE
  1313. END;
  1314. i := have; j := 0;
  1315. stkArg[d][i] := cls; INC(i);
  1316. stkArg[d][i] := " "; INC(i);
  1317. WHILE q[j] # CHR(0) DO
  1318. stkArg[d][i] := q[j]; INC(i); INC(j)
  1319. END;
  1320. stkArg[d][i] := ","; INC(i);
  1321. stkArg[d][i] := " "; INC(i);
  1322. stkArg[d][i] := CHR(0);
  1323. INC(stkN[d]);
  1324. RETURN TRUE
  1325. END CallArg;
  1326. PROCEDURE CallEnd (wantRes: BOOLEAN; VAR q: QVal);
  1327. VAR i, d : CARDINAL;
  1328. rc : CHAR;
  1329. body : ARRAY [0 .. 1023] OF CHAR;
  1330. BEGIN
  1331. IF callDepth = 0 THEN Cpy(q, "0"); RETURN END;
  1332. DEC(callDepth);
  1333. d := callDepth;
  1334. IF d > HIGH(stkArg) THEN d := HIGH(stkArg) END;
  1335. Cpy(funcName, stkName[d]);
  1336. rc := stkRes[d];
  1337. i := 0;
  1338. WHILE (stkArg[d][i] # CHR(0)) AND (i < HIGH(body)) DO
  1339. body[i] := stkArg[d][i]; INC(i)
  1340. END;
  1341. IF (i >= 2) AND (body[i-2] = ",") THEN
  1342. body[i-2] := CHR(0)
  1343. ELSE
  1344. body[i] := CHR(0)
  1345. END;
  1346. Revive;
  1347. IF wantRes THEN
  1348. NewTemp(q);
  1349. W(" "); W(q); W(" ="); Wc(rc)
  1350. ELSE
  1351. W(" ")
  1352. END;
  1353. IF stkInd[d] THEN
  1354. W(" call "); W(funcName); W("(") (* callee operand already %-prefixed *)
  1355. ELSE
  1356. W(" call $"); W(funcName); W("(")
  1357. END;
  1358. IF stkExt[d] THEN
  1359. W(body) (* C callee: no static link *)
  1360. ELSE
  1361. W("l "); W(stkLink[d]);
  1362. IF body[0] # CHR(0) THEN W(", "); W(body) END
  1363. END;
  1364. WL(")")
  1365. END CallEnd;
  1366. PROCEDURE InitStack (addr: ARRAY OF CHAR; t: INTEGER);
  1367. BEGIN
  1368. useStack := TRUE;
  1369. InitHeap(addr, t);
  1370. useStack := FALSE
  1371. END InitStack;
  1372. PROCEDURE ArgClass (t: INTEGER): CHAR;
  1373. (* Formal/actual class for call emission. *)
  1374. BEGIN
  1375. RETURN ResClass(t)
  1376. END ArgClass;
  1377. PROCEDURE CArg (q: ARRAY OF CHAR; t: INTEGER; VAR out: QVal;
  1378. VAR cls: CHAR);
  1379. BEGIN
  1380. cls := ArgClass(t);
  1381. IF SymTab.ClassOf(t) = SymTab.ClStr THEN
  1382. NewTemp(out); Op3L("add", out, q, "8"); cls := "l"
  1383. ELSIF (SymTab.ClassOf(t) = SymTab.ClArray)
  1384. AND SymTab.IsCharArray(t) THEN
  1385. NewTemp(out); Op3L("add", out, q, "8"); cls := "l"
  1386. ELSE Cpy(out, q)
  1387. END
  1388. END CArg;
  1389. PROCEDURE CArgAdd (q: ARRAY OF CHAR; t: INTEGER; VAR out: QVal;
  1390. VAR cls: CHAR);
  1391. BEGIN
  1392. CArg(q, t, out, cls);
  1393. IF NOT CallArg(out, cls) THEN END
  1394. END CArgAdd;
  1395. (* ---------------- temporaries and operators ---------------- *)
  1396. PROCEDURE NewTemp (VAR t: QVal);
  1397. BEGIN
  1398. Cpy(t, "%t");
  1399. AppNum(t, nTemp);
  1400. INC(nTemp)
  1401. END NewTemp;
  1402. PROCEDURE LocLoadFlat (idx: INTEGER; long: BOOLEAN; VAR q: QVal);
  1403. (* Loads a same-scope entry: slots/data by class (or long for
  1404. pointers), addr entries via ElemLoad. *)
  1405. BEGIN
  1406. IF locTag[idx] = 2 THEN
  1407. ElemLoad(locRep[idx], locTyp[idx], q);
  1408. RETURN
  1409. END;
  1410. NewTemp(q);
  1411. Revive;
  1412. W(" "); W(q);
  1413. IF long THEN W(" =l loadl ")
  1414. ELSIF locCls[idx] = "d" THEN W(" =d loadd ")
  1415. ELSE W(" =w loadw ")
  1416. END;
  1417. IF locTag[idx] = 1 THEN W("$") END;
  1418. WL(locRep[idx])
  1419. END LocLoadFlat;
  1420. PROCEDURE LocStoreFlat (idx: INTEGER; q: ARRAY OF CHAR; long: BOOLEAN);
  1421. (* Stores a same-scope entry: slots/data by class (or long), addr
  1422. via ElemStore. Data stores are grammar-unreachable (consts). *)
  1423. BEGIN
  1424. IF locTag[idx] = 2 THEN
  1425. ElemStore(locRep[idx], q, locTyp[idx]);
  1426. RETURN
  1427. END;
  1428. Revive;
  1429. IF long THEN W(" storel ")
  1430. ELSIF locCls[idx] = "d" THEN W(" stored ")
  1431. ELSE W(" storew ")
  1432. END;
  1433. W(q); W(", ");
  1434. IF locTag[idx] = 1 THEN W("$") END;
  1435. WL(locRep[idx])
  1436. END LocStoreFlat;
  1437. PROCEDURE UpLoad (flat: INTEGER; levels: CARDINAL; long: BOOLEAN;
  1438. VAR q: QVal);
  1439. (* Up-level load: data entries live globally; otherwise the link
  1440. cell yields the address and ElemLoad reads through it. *)
  1441. VAR a : QVal;
  1442. rel : CARDINAL;
  1443. BEGIN
  1444. IF locTag[flat] = 1 THEN
  1445. NewTemp(q);
  1446. Revive;
  1447. W(" "); W(q);
  1448. IF long THEN W(" =l loadl $")
  1449. ELSIF locCls[flat] = "d" THEN W(" =d loadd $")
  1450. ELSE W(" =w loadw $")
  1451. END;
  1452. WL(locRep[flat]);
  1453. RETURN
  1454. END;
  1455. rel := VAL(CARDINAL, flat) - scopeBase[scopeTop - 1 - levels];
  1456. UpAddr(levels, rel, a);
  1457. ElemLoad(a, locTyp[flat], q)
  1458. END UpLoad;
  1459. PROCEDURE UpStore (flat: INTEGER; levels: CARDINAL; q: ARRAY OF CHAR;
  1460. long: BOOLEAN);
  1461. VAR a : QVal;
  1462. rel : CARDINAL;
  1463. BEGIN
  1464. IF locTag[flat] = 1 THEN
  1465. Revive;
  1466. IF long THEN W(" storel ")
  1467. ELSIF locCls[flat] = "d" THEN W(" stored ")
  1468. ELSE W(" storew ")
  1469. END;
  1470. W(q); W(", $"); WL(locRep[flat]);
  1471. RETURN
  1472. END;
  1473. rel := VAL(CARDINAL, flat) - scopeBase[scopeTop - 1 - levels];
  1474. UpAddr(levels, rel, a);
  1475. ElemStore(a, q, locTyp[flat])
  1476. END UpStore;
  1477. PROCEDURE UpAddrOf (flat: INTEGER; levels: CARDINAL; VAR q: QVal);
  1478. (* Up-level address: data entries name globals; otherwise the
  1479. link cell holds it (composites) or points at it. For scalars
  1480. the cell IS the usable address (slot or actual address). *)
  1481. VAR a : QVal;
  1482. rel : CARDINAL;
  1483. BEGIN
  1484. IF locTag[flat] = 1 THEN
  1485. Cpy(q, "$");
  1486. App(q, locRep[flat]);
  1487. RETURN
  1488. END;
  1489. rel := VAL(CARDINAL, flat) - scopeBase[scopeTop - 1 - levels];
  1490. UpAddr(levels, rel, q)
  1491. END UpAddrOf;
  1492. PROCEDURE LoadDesignator (name: ARRAY OF CHAR; t: INTEGER; k: INTEGER;
  1493. VAR q: QVal): BOOLEAN;
  1494. VAR cls : INTEGER;
  1495. BEGIN
  1496. cls := SymTab.ClassOf(t);
  1497. IF SymTab.Equal(name, "TRUE") THEN
  1498. Cpy(q, "1"); RETURN TRUE
  1499. ELSIF SymTab.Equal(name, "FALSE") THEN
  1500. Cpy(q, "0"); RETURN TRUE
  1501. ELSIF SymTab.Equal(name, "NIL") THEN
  1502. Cpy(q, "0"); RETURN TRUE
  1503. END;
  1504. IF (k = SymTab.KindVar) OR (k = SymTab.KindParam)
  1505. OR (k = SymTab.KindConst) THEN
  1506. IF (cls = SymTab.ClInt) OR (cls = SymTab.ClBool)
  1507. OR (cls = SymTab.ClChar) OR (cls = SymTab.ClReal) THEN
  1508. LoadVar(name, cls = SymTab.ClReal, q); RETURN TRUE
  1509. ELSIF (cls = SymTab.ClPtr) OR (cls = SymTab.ClProc) THEN
  1510. LoadPtr(name, q); RETURN TRUE
  1511. ELSIF (cls = SymTab.ClArray) OR (cls = SymTab.ClSet)
  1512. OR (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  1513. AddrOf(name, q); RETURN TRUE
  1514. END
  1515. END;
  1516. Cpy(q, "0");
  1517. RETURN FALSE
  1518. END LoadDesignator;
  1519. PROCEDURE LoadVar (name: ARRAY OF CHAR; isReal: BOOLEAN; VAR q: QVal);
  1520. VAR idx : INTEGER;
  1521. levels : CARDINAL;
  1522. ref : QVal;
  1523. BEGIN
  1524. IF LocFindUp(name, levels, idx) THEN
  1525. IF levels = 0 THEN LocLoadFlat(idx, FALSE, q)
  1526. ELSE UpLoad(idx, levels, FALSE, q)
  1527. END;
  1528. RETURN
  1529. END;
  1530. NewTemp(q);
  1531. Revive;
  1532. SymRef(name, ref);
  1533. W(" "); W(q);
  1534. IF isReal THEN W(" =d loadd ") ELSE W(" =w loadw ") END;
  1535. WL(ref)
  1536. END LoadVar;
  1537. PROCEDURE LoadPtr (name: ARRAY OF CHAR; VAR q: QVal);
  1538. VAR idx : INTEGER;
  1539. levels : CARDINAL;
  1540. ref : QVal;
  1541. BEGIN
  1542. IF LocFindUp(name, levels, idx) THEN
  1543. IF levels = 0 THEN LocLoadFlat(idx, TRUE, q)
  1544. ELSE UpLoad(idx, levels, TRUE, q)
  1545. END;
  1546. RETURN
  1547. END;
  1548. NewTemp(q);
  1549. Revive;
  1550. SymRef(name, ref);
  1551. W(" "); W(q); W(" =l loadl ");
  1552. WL(ref)
  1553. END LoadPtr;
  1554. PROCEDURE LoadLong (name: ARRAY OF CHAR; VAR q: QVal);
  1555. VAR idx : INTEGER;
  1556. levels : CARDINAL;
  1557. ref : QVal;
  1558. BEGIN
  1559. IF LocFindUp(name, levels, idx) THEN
  1560. IF levels = 0 THEN LocLoadFlat(idx, TRUE, q)
  1561. ELSE UpLoad(idx, levels, TRUE, q)
  1562. END;
  1563. RETURN
  1564. END;
  1565. NewTemp(q);
  1566. Revive;
  1567. SymRef(name, ref);
  1568. W(" "); W(q); W(" =l loadl ");
  1569. WL(ref)
  1570. END LoadLong;
  1571. PROCEDURE StoreLong (name: ARRAY OF CHAR; q: ARRAY OF CHAR);
  1572. VAR idx : INTEGER;
  1573. levels : CARDINAL;
  1574. ref : QVal;
  1575. BEGIN
  1576. IF LocFindUp(name, levels, idx) THEN
  1577. IF levels = 0 THEN LocStoreFlat(idx, q, TRUE)
  1578. ELSE UpStore(idx, levels, q, TRUE)
  1579. END;
  1580. RETURN
  1581. END;
  1582. Revive;
  1583. SymRef(name, ref);
  1584. W(" storel "); W(q); W(", "); WL(ref)
  1585. END StoreLong;
  1586. PROCEDURE WidenLong (a: ARRAY OF CHAR; VAR q: QVal);
  1587. (* INTEGER (w) -> LONGINT (l) via extsw. *)
  1588. BEGIN
  1589. NewTemp(q);
  1590. Revive;
  1591. W(" "); W(q); W(" =l extsw "); WL(a)
  1592. END WidenLong;
  1593. PROCEDURE CmpLong (op: INTEGER; l, r: ARRAY OF CHAR; VAR q: QVal);
  1594. (* 64-bit comparison (result w). *)
  1595. VAR mn : ARRAY [0 .. 7] OF CHAR;
  1596. BEGIN
  1597. mn[0] := CHR(0);
  1598. IF op = SymTab.OpEq THEN Cpy(mn, "ceql")
  1599. ELSIF (op = SymTab.OpNeq1) OR (op = SymTab.OpNeq2) THEN Cpy(mn, "cnel")
  1600. ELSIF op = SymTab.OpLt THEN Cpy(mn, "csltl")
  1601. ELSIF op = SymTab.OpLe THEN Cpy(mn, "cslel")
  1602. ELSIF op = SymTab.OpGt THEN Cpy(mn, "csgtl")
  1603. ELSE Cpy(mn, "csgel")
  1604. END;
  1605. NewTemp(q);
  1606. Op3(mn, q, l, r, FALSE)
  1607. END CmpLong;
  1608. PROCEDURE StoreVar (name: ARRAY OF CHAR; q: ARRAY OF CHAR; isReal: BOOLEAN);
  1609. VAR idx : INTEGER;
  1610. levels : CARDINAL;
  1611. ref : QVal;
  1612. BEGIN
  1613. IF LocFindUp(name, levels, idx) THEN
  1614. IF levels = 0 THEN LocStoreFlat(idx, q, FALSE)
  1615. ELSE UpStore(idx, levels, q, FALSE)
  1616. END;
  1617. RETURN
  1618. END;
  1619. Revive;
  1620. SymRef(name, ref);
  1621. IF isReal THEN W(" stored ") ELSE W(" storew ") END;
  1622. W(q); W(", "); WL(ref)
  1623. END StoreVar;
  1624. PROCEDURE StorePtr (name: ARRAY OF CHAR; q: ARRAY OF CHAR);
  1625. VAR idx : INTEGER;
  1626. levels : CARDINAL;
  1627. ref : QVal;
  1628. BEGIN
  1629. IF LocFindUp(name, levels, idx) THEN
  1630. IF levels = 0 THEN LocStoreFlat(idx, q, TRUE)
  1631. ELSE UpStore(idx, levels, q, TRUE)
  1632. END;
  1633. RETURN
  1634. END;
  1635. Revive;
  1636. SymRef(name, ref);
  1637. W(" storel "); W(q); W(", "); WL(ref)
  1638. END StorePtr;
  1639. PROCEDURE Op3 (mn: ARRAY OF CHAR; res, l, r: ARRAY OF CHAR;
  1640. isReal: BOOLEAN);
  1641. BEGIN
  1642. Revive;
  1643. W(" "); W(res);
  1644. IF isReal THEN W(" =d ") ELSE W(" =w ") END;
  1645. W(mn); W(" "); W(l); W(", "); WL(r)
  1646. END Op3;
  1647. PROCEDURE AbsQ (a: ARRAY OF CHAR; VAR q: QVal; isReal: BOOLEAN);
  1648. VAR c, neg : QVal;
  1649. lNeg, lPos, lDone : QVal;
  1650. BEGIN
  1651. IF isReal THEN Cmp(SymTab.OpLt, a, "d_0.0", c, TRUE)
  1652. ELSE Cmp(SymTab.OpLt, a, "0", c, FALSE)
  1653. END;
  1654. NewLabel(lNeg); NewLabel(lPos); NewLabel(lDone);
  1655. NewTemp(neg);
  1656. NegQ(a, neg, isReal);
  1657. Jnz(c, lNeg, lPos);
  1658. EmitLabel(lNeg);
  1659. CopyOp(neg, q);
  1660. Jmp(lDone);
  1661. EmitLabel(lPos);
  1662. CopyOp(a, q);
  1663. EmitLabel(lDone)
  1664. END AbsQ;
  1665. PROCEDURE NegQ (a: ARRAY OF CHAR; VAR q: QVal; isReal: BOOLEAN);
  1666. BEGIN
  1667. NewTemp(q);
  1668. IF isReal THEN Op3("sub", q, "d_0.0", a, TRUE)
  1669. ELSE Op3("sub", q, "0", a, FALSE)
  1670. END
  1671. END NegQ;
  1672. PROCEDURE ConvIR (a: ARRAY OF CHAR; VAR q: QVal);
  1673. (* INTEGER -> REAL widening for assignments (swtof). *)
  1674. BEGIN
  1675. NewTemp(q);
  1676. Revive;
  1677. W(" "); W(q); W(" =d swtof "); WL(a)
  1678. END ConvIR;
  1679. PROCEDURE NarrowLong (a: ARRAY OF CHAR; VAR q: QVal);
  1680. (* LONGINT (l) -> INTEGER (w): keep the low 32 bits. *)
  1681. BEGIN
  1682. NewTemp(q);
  1683. Revive;
  1684. W(" "); W(q); W(" =w copy "); WL(a)
  1685. END NarrowLong;
  1686. PROCEDURE ConvRI (a: ARRAY OF CHAR; VAR q: QVal);
  1687. (* REAL -> INTEGER (truncate toward zero). *)
  1688. BEGIN
  1689. NewTemp(q);
  1690. Revive;
  1691. W(" "); W(q); W(" =w dtosi "); WL(a)
  1692. END ConvRI;
  1693. PROCEDURE ConvRL (a: ARRAY OF CHAR; VAR q: QVal);
  1694. (* REAL -> LONGINT (truncate toward zero). *)
  1695. BEGIN
  1696. NewTemp(q);
  1697. Revive;
  1698. W(" "); W(q); W(" =l dtosi "); WL(a)
  1699. END ConvRL;
  1700. PROCEDURE ConvLR (a: ARRAY OF CHAR; VAR q: QVal);
  1701. (* LONGINT -> REAL. *)
  1702. BEGIN
  1703. NewTemp(q);
  1704. Revive;
  1705. W(" "); W(q); W(" =d sltof "); WL(a)
  1706. END ConvLR;
  1707. PROCEDURE NotQ (a: ARRAY OF CHAR; VAR q: QVal);
  1708. BEGIN
  1709. NewTemp(q);
  1710. Op3("xor", q, "1", a, FALSE)
  1711. END NotQ;
  1712. PROCEDURE CapQ (a: ARRAY OF CHAR; VAR q: QVal);
  1713. (* Branchless UPCASE: q := a - 32 * (a >= 'a' AND a <= 'z'). *)
  1714. VAR lo, hi, in1, delta: QVal;
  1715. BEGIN
  1716. NewTemp(lo); Op3("csgew", lo, a, "97", FALSE);
  1717. NewTemp(hi); Op3("cslew", hi, a, "122", FALSE);
  1718. NewTemp(in1); Op3("and", in1, lo, hi, FALSE);
  1719. NewTemp(delta); Op3("mul", delta, in1, "32", FALSE);
  1720. NewTemp(q); Op3("sub", q, a, delta, FALSE)
  1721. END CapQ;
  1722. PROCEDURE NewLabel (VAR l: QVal);
  1723. BEGIN
  1724. Cpy(l, "@L");
  1725. AppNum(l, nLab);
  1726. INC(nLab)
  1727. END NewLabel;
  1728. PROCEDURE Revive;
  1729. (* Opens an unreachable block if past a terminator. Keeps every
  1730. temporary defined and every instruction inside a block, even for
  1731. dead source tails (EXIT followed by more statements) and CASE
  1732. chains whose compares follow a Jmp. Counter-driven: fixpoint-safe. *)
  1733. VAR b: QVal;
  1734. BEGIN
  1735. IF opened AND dead THEN
  1736. NewLabel(b);
  1737. WL(b);
  1738. dead := FALSE
  1739. END
  1740. END Revive;
  1741. PROCEDURE EmitLabel (l: ARRAY OF CHAR);
  1742. BEGIN
  1743. WL(l);
  1744. dead := FALSE
  1745. END EmitLabel;
  1746. PROCEDURE Jmp (l: ARRAY OF CHAR);
  1747. BEGIN
  1748. IF dead THEN RETURN END; (* unreachable: a terminator with no
  1749. intervening label can never be reached; emitting it would end
  1750. the block and orphan whatever follows. *)
  1751. W(" jmp "); WL(l);
  1752. dead := TRUE
  1753. END Jmp;
  1754. PROCEDURE Jnz (c, t, f: ARRAY OF CHAR);
  1755. BEGIN
  1756. IF dead THEN RETURN END;
  1757. W(" jnz "); W(c); W(", "); W(t); W(", "); WL(f);
  1758. dead := TRUE
  1759. END Jnz;
  1760. PROCEDURE Cmp (op: INTEGER; l, r: ARRAY OF CHAR; VAR q: QVal;
  1761. isReal: BOOLEAN);
  1762. VAR mn : ARRAY [0 .. 7] OF CHAR;
  1763. BEGIN
  1764. mn[0] := CHR(0);
  1765. IF isReal THEN
  1766. IF op = SymTab.OpEq THEN Cpy(mn, "ceqd")
  1767. ELSIF (op = SymTab.OpNeq1) OR (op = SymTab.OpNeq2) THEN Cpy(mn, "cned")
  1768. ELSIF op = SymTab.OpLt THEN Cpy(mn, "cltd")
  1769. ELSIF op = SymTab.OpLe THEN Cpy(mn, "cled")
  1770. ELSIF op = SymTab.OpGt THEN Cpy(mn, "cgtd")
  1771. ELSE Cpy(mn, "cged")
  1772. END
  1773. ELSE
  1774. IF op = SymTab.OpEq THEN Cpy(mn, "ceqw")
  1775. ELSIF (op = SymTab.OpNeq1) OR (op = SymTab.OpNeq2) THEN Cpy(mn, "cnew")
  1776. ELSIF op = SymTab.OpLt THEN Cpy(mn, "csltw")
  1777. ELSIF op = SymTab.OpLe THEN Cpy(mn, "cslew")
  1778. ELSIF op = SymTab.OpGt THEN Cpy(mn, "csgtw")
  1779. ELSE Cpy(mn, "csgew")
  1780. END
  1781. END;
  1782. NewTemp(q);
  1783. Op3(mn, q, l, r, FALSE)
  1784. END Cmp;
  1785. PROCEDURE StrEq (op: INTEGER; l, r: ARRAY OF CHAR; VAR q: QVal);
  1786. (* String equality via the shim: q := (l = r) as a BOOLEAN, negated for
  1787. OpNeq. Operands are descriptor addresses; the shim strcmps the
  1788. NUL-terminated contents. *)
  1789. VAR t: QVal;
  1790. BEGIN
  1791. NewTemp(q);
  1792. W(" "); W(q); W(" =w call $m2streq(l ");
  1793. W(l); W(", l "); W(r); WL(")");
  1794. IF (op = SymTab.OpNeq1) OR (op = SymTab.OpNeq2) THEN
  1795. NewTemp(t);
  1796. W(" "); W(t); W(" =w xor "); W(q); WL(", 1");
  1797. Cpy(q, t)
  1798. END
  1799. END StrEq;
  1800. PROCEDURE StrAssign (dst, src: ARRAY OF CHAR);
  1801. (* Copy the string src into the CHAR array at dst (content copy,
  1802. truncated to the destination capacity, NUL-terminated). *)
  1803. BEGIN
  1804. W(" call $m2strassign(l "); W(dst); W(", l "); W(src); WL(")")
  1805. END StrAssign;
  1806. PROCEDURE UAssign (dst, src: ARRAY OF CHAR);
  1807. (* Copy a U"..." descriptor (count + 4-byte codepoints) into the UCHAR
  1808. array at dst: min(count, capacity) codepoints + a 0 terminator. *)
  1809. BEGIN
  1810. W(" call $m2uassign(l "); W(dst); W(", l "); W(src); WL(")")
  1811. END UAssign;
  1812. PROCEDURE UStrLen (s: ARRAY OF CHAR; VAR q: QVal);
  1813. (* q := the UString codepoint count (INTEGER). *)
  1814. VAR t: QVal;
  1815. BEGIN
  1816. NewTemp(t);
  1817. W(" "); W(t); W(" =w call $m2ustrlen(l "); W(s); WL(")");
  1818. Cpy(q, t)
  1819. END UStrLen;
  1820. PROCEDURE DecQ (VAR q: QVal);
  1821. (* q := q - 1 (w domain; q is an INTEGER count). *)
  1822. VAR t: QVal;
  1823. BEGIN
  1824. NewTemp(t);
  1825. Op3("sub", t, q, "1", FALSE);
  1826. Cpy(q, t)
  1827. END DecQ;
  1828. PROCEDURE UStrCat (a, b: ARRAY OF CHAR; VAR q: QVal);
  1829. (* q := a + b (a UString descriptor in the shim's concat buffer). *)
  1830. BEGIN
  1831. NewTemp(q);
  1832. W(" "); W(q); W(" =l call $m2ustrcat(l "); W(a); W(", l "); W(b); WL(")")
  1833. END UStrCat;
  1834. PROCEDURE UStrFrom (cp: ARRAY OF CHAR; VAR q: QVal);
  1835. (* q := a 1-codepoint UString descriptor for the UCHAR value cp. *)
  1836. BEGIN
  1837. NewTemp(q);
  1838. W(" "); W(q); W(" =l call $m2ustrfrom(l "); W(cp); WL(")")
  1839. END UStrFrom;
  1840. PROCEDURE StrCat (a, b: ARRAY OF CHAR; VAR q: QVal);
  1841. (* q := a + b (a descriptor in the shim's concat buffer). *)
  1842. BEGIN
  1843. NewTemp(q);
  1844. W(" "); W(q); W(" =l call $m2strcat(l "); W(a); W(", l "); W(b); WL(")")
  1845. END StrCat;
  1846. (* ---------------- arrays: length-prefixed layout ---------------- *)
  1847. (* Indexes and counts are LONGCARD (l) in emitted code; immediates
  1848. pass through, w-temps widen via extsw. Traps call $abort + hlt
  1849. (interim; runtime/syslib/Trap replaces them later). *)
  1850. PROCEDURE ElemCls (t: SymTab.TypeIndex): INTEGER;
  1851. BEGIN
  1852. RETURN SymTab.ClassOf(SymTab.ArrayElem(t))
  1853. END ElemCls;
  1854. PROCEDURE ElemSize (t: SymTab.TypeIndex): CARDINAL;
  1855. (* Storage size of t's elements: CHAR 1, REAL 8, nested/pointer/long 8,
  1856. records/classes/sets their inline footprint, else 4. t is an array
  1857. (or string) descriptor. *)
  1858. VAR cls: INTEGER; et: SymTab.TypeIndex;
  1859. BEGIN
  1860. cls := SymTab.ClassOf(t);
  1861. IF cls = SymTab.ClStr THEN RETURN 1 END;
  1862. et := SymTab.ArrayElem(t);
  1863. cls := SymTab.ClassOf(et);
  1864. IF cls = SymTab.ClChar THEN RETURN 1
  1865. ELSIF cls = SymTab.ClReal THEN RETURN 8
  1866. ELSIF (cls = SymTab.ClArray) OR (cls = SymTab.ClPtr)
  1867. OR (cls = SymTab.ClLong) THEN RETURN 8
  1868. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass)
  1869. OR (cls = SymTab.ClSet) THEN
  1870. RETURN SymTab.TypeSize(et)
  1871. ELSE RETURN 4
  1872. END
  1873. END ElemSize;
  1874. PROCEDURE Op3L (mn: ARRAY OF CHAR; res, l, r: ARRAY OF CHAR);
  1875. BEGIN
  1876. Revive;
  1877. W(" "); W(res);
  1878. W(" =l ");
  1879. W(mn); W(" "); W(l); W(", "); WL(r)
  1880. END Op3L;
  1881. PROCEDURE HaltQ;
  1882. BEGIN
  1883. Revive;
  1884. WL(" call $exit(w 1)");
  1885. WL(" hlt");
  1886. dead := TRUE
  1887. END HaltQ;
  1888. PROCEDURE Trap;
  1889. BEGIN
  1890. Revive;
  1891. WL(" call $abort()");
  1892. WL(" hlt");
  1893. dead := TRUE
  1894. END Trap;
  1895. (* ---------------- records: flat blobs, pointer fields ---------------- *)
  1896. (* Scalars/sets inline, array fields as inline descriptors (8-byte length
  1897. word + data), nested records inline. Static offsets throughout. *)
  1898. PROCEDURE HasNestedArray (t: SymTab.TypeIndex): BOOLEAN;
  1899. (* TRUE when record type t (recursively) has an ARRAY OF ARRAY field,
  1900. i.e. a field whose element is itself an array. Only such record
  1901. arrays need per-element expansion (their nested sub-descriptors must
  1902. be emitted); plain record arrays stay a compact zero blob. *)
  1903. VAR i: CARDINAL; fn: SymTab.Name; ft, et: SymTab.TypeIndex; cls: INTEGER;
  1904. BEGIN
  1905. i := 0;
  1906. WHILE i < SymTab.FieldCount(t) DO
  1907. SymTab.FieldName(t, i, fn);
  1908. ft := SymTab.FieldType(t, fn);
  1909. cls := SymTab.ClassOf(ft);
  1910. IF cls = SymTab.ClArray THEN
  1911. et := SymTab.ArrayElem(ft);
  1912. IF SymTab.ClassOf(et) = SymTab.ClArray THEN RETURN TRUE END
  1913. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  1914. IF HasNestedArray(ft) THEN RETURN TRUE END
  1915. END;
  1916. INC(i)
  1917. END;
  1918. RETURN FALSE
  1919. END HasNestedArray;
  1920. PROCEDURE ArrBodyItems (prefix: ARRAY OF CHAR; t: INTEGER);
  1921. (* Inline contents of an array descriptor (no "data $name = {" wrapper
  1922. and no closing brace): "l <n>[, <elem>...]". Nested levels are
  1923. referenced as $prefix_i (emitted by ArrData). Record/class elements
  1924. with nested-array fields are expanded per element (EmitRec) so their
  1925. sub-descriptors are referenced; others stay a compact zero blob. *)
  1926. VAR n, i: CARDINAL;
  1927. elem: SymTab.TypeIndex;
  1928. ecls: INTEGER;
  1929. esz: CARDINAL;
  1930. bv, sub: QVal;
  1931. first: BOOLEAN;
  1932. (* One inline record: mirrors RecItems (same field order and comma
  1933. handling), but array fields recurse into the enclosing
  1934. ArrBodyItems. Nested here so no FORWARD declaration is needed. *)
  1935. PROCEDURE EmitRec (rt: SymTab.TypeIndex; rpre: ARRAY OF CHAR;
  1936. VAR first: BOOLEAN);
  1937. VAR i, k, w: CARDINAL;
  1938. fn: SymTab.Name;
  1939. ft: SymTab.TypeIndex;
  1940. cls: INTEGER;
  1941. rsub: QVal;
  1942. PROCEDURE Sep;
  1943. BEGIN
  1944. IF first THEN first := FALSE ELSE W(", ") END
  1945. END Sep;
  1946. BEGIN
  1947. i := 0;
  1948. WHILE i < SymTab.FieldCount(rt) DO
  1949. SymTab.FieldName(rt, i, fn);
  1950. ft := SymTab.FieldType(rt, fn);
  1951. cls := SymTab.ClassOf(ft);
  1952. IF cls = SymTab.ClReal THEN Sep; W("d 0")
  1953. ELSIF cls = SymTab.ClChar THEN Sep; W("b 0")
  1954. ELSIF cls = SymTab.ClArray THEN
  1955. Sep;
  1956. Cpy(rsub, rpre); App(rsub, "_"); App(rsub, fn);
  1957. ArrBodyItems(rsub, ft)
  1958. ELSIF cls = SymTab.ClSet THEN
  1959. w := SymTab.SetWords(ft);
  1960. IF w = 0 THEN w := 1 END;
  1961. Sep; W("w 0");
  1962. k := 1;
  1963. WHILE k < w DO W(", w 0"); INC(k) END
  1964. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  1965. Cpy(rsub, rpre); App(rsub, "_"); App(rsub, fn);
  1966. EmitRec(ft, rsub, first)
  1967. ELSE Sep; W("w 0")
  1968. END;
  1969. INC(i)
  1970. END
  1971. END EmitRec;
  1972. BEGIN
  1973. n := SymTab.ArrayLen(t);
  1974. elem := SymTab.ArrayElem(t);
  1975. ecls := SymTab.ClassOf(elem);
  1976. W("l ");
  1977. IntStr(VAL(INTEGER, n), bv);
  1978. W(bv);
  1979. IF ecls = SymTab.ClArray THEN
  1980. i := 0;
  1981. WHILE i < n DO
  1982. W(", l $"); W(prefix); W("_");
  1983. IntStr(VAL(INTEGER, i), bv);
  1984. W(bv);
  1985. INC(i)
  1986. END
  1987. ELSIF ((ecls = SymTab.ClRecord) OR (ecls = SymTab.ClClass))
  1988. AND HasNestedArray(elem) THEN
  1989. (* Per-element record expansion: nested-array field descriptors. *)
  1990. i := 0;
  1991. WHILE i < n DO
  1992. Cpy(sub, prefix); App(sub, "_");
  1993. IntStr(VAL(INTEGER, i), bv); App(sub, bv);
  1994. first := FALSE;
  1995. EmitRec(elem, sub, first);
  1996. INC(i)
  1997. END
  1998. ELSE
  1999. IF ecls = SymTab.ClChar THEN
  2000. (* CHAR: n data bytes + one NUL terminator slot *)
  2001. W(", z ");
  2002. IntStr(VAL(INTEGER, n + 1), bv);
  2003. W(bv)
  2004. ELSIF ecls = SymTab.ClUChar THEN
  2005. (* UCHAR: n codepoints + one 0-codepoint terminator slot *)
  2006. W(", z ");
  2007. IntStr(VAL(INTEGER, (n + 1) * 4), bv);
  2008. W(bv)
  2009. ELSE
  2010. IF (ecls = SymTab.ClReal) OR (ecls = SymTab.ClPtr)
  2011. OR (ecls = SymTab.ClProc) OR (ecls = SymTab.ClLong) THEN esz := 8
  2012. ELSIF (ecls = SymTab.ClRecord) OR (ecls = SymTab.ClClass)
  2013. OR (ecls = SymTab.ClSet) THEN
  2014. (* inline footprint, not a fixed 4 (same fix as ElemSize) *)
  2015. esz := SymTab.TypeSize(elem)
  2016. ELSE esz := 4
  2017. END;
  2018. IF n > 0 THEN
  2019. W(", z ");
  2020. IntStr(VAL(INTEGER, n * esz), bv);
  2021. W(bv)
  2022. END
  2023. END
  2024. END
  2025. END ArrBodyItems;
  2026. PROCEDURE ArrData (name: ARRAY OF CHAR; t: INTEGER);
  2027. (* "data $name = { <items> }" plus, for nested levels, the recursive
  2028. sub-descriptor data $name_i. *)
  2029. VAR n, i: CARDINAL;
  2030. elem: SymTab.TypeIndex;
  2031. bv: QVal;
  2032. sub: QVal;
  2033. (* Mirrors RecStatics, but nested-array sub-descriptors recurse into
  2034. the enclosing ArrData. Nested here so no FORWARD is needed. *)
  2035. PROCEDURE EmitRecStatics (rpre: ARRAY OF CHAR; rt: SymTab.TypeIndex);
  2036. VAR k, j, an: CARDINAL;
  2037. fn: SymTab.Name;
  2038. ft, et: SymTab.TypeIndex;
  2039. cls: INTEGER;
  2040. rsub, bv2: QVal;
  2041. BEGIN
  2042. k := 0;
  2043. WHILE k < SymTab.FieldCount(rt) DO
  2044. SymTab.FieldName(rt, k, fn);
  2045. ft := SymTab.FieldType(rt, fn);
  2046. cls := SymTab.ClassOf(ft);
  2047. IF cls = SymTab.ClArray THEN
  2048. et := SymTab.ArrayElem(ft);
  2049. IF SymTab.ClassOf(et) = SymTab.ClArray THEN
  2050. an := SymTab.ArrayLen(ft);
  2051. j := 0;
  2052. WHILE j < an DO
  2053. Cpy(rsub, rpre); App(rsub, "_"); App(rsub, fn);
  2054. App(rsub, "_");
  2055. IntStr(VAL(INTEGER, j), bv2);
  2056. App(rsub, bv2);
  2057. ArrData(rsub, et);
  2058. INC(j)
  2059. END
  2060. END
  2061. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  2062. Cpy(rsub, rpre); App(rsub, "_"); App(rsub, fn);
  2063. EmitRecStatics(rsub, ft)
  2064. END;
  2065. INC(k)
  2066. END
  2067. END EmitRecStatics;
  2068. BEGIN
  2069. IF NOT opened THEN RETURN END;
  2070. W("data $"); W(name);
  2071. W(" = { ");
  2072. ArrBodyItems(name, t);
  2073. WL(" }");
  2074. elem := SymTab.ArrayElem(t);
  2075. IF SymTab.ClassOf(elem) = SymTab.ClArray THEN
  2076. n := SymTab.ArrayLen(t);
  2077. i := 0;
  2078. WHILE i < n DO
  2079. Cpy(sub, name); App(sub, "_");
  2080. IntStr(VAL(INTEGER, i), bv);
  2081. App(sub, bv);
  2082. ArrData(sub, elem);
  2083. INC(i)
  2084. END
  2085. ELSIF ((SymTab.ClassOf(elem) = SymTab.ClRecord)
  2086. OR (SymTab.ClassOf(elem) = SymTab.ClClass))
  2087. AND HasNestedArray(elem) THEN
  2088. (* Sub-descriptors referenced (as $name_i_field_k) from the inline
  2089. items emitted by ArrBodyItems; one set per element. *)
  2090. n := SymTab.ArrayLen(t);
  2091. i := 0;
  2092. WHILE i < n DO
  2093. Cpy(sub, name); App(sub, "_");
  2094. IntStr(VAL(INTEGER, i), bv);
  2095. App(sub, bv);
  2096. EmitRecStatics(sub, elem);
  2097. INC(i)
  2098. END
  2099. END
  2100. END ArrData;
  2101. PROCEDURE RecStatics (recname: ARRAY OF CHAR; t: SymTab.TypeIndex);
  2102. (* Pre-pass: static sub-descriptors for NESTED array fields (the
  2103. top-level array field is now inline in the record), recursive. *)
  2104. VAR n, i, k, an: CARDINAL;
  2105. fn: SymTab.Name;
  2106. ft, et: SymTab.TypeIndex;
  2107. cls: INTEGER;
  2108. sub, bv: QVal;
  2109. BEGIN
  2110. n := SymTab.FieldCount(t);
  2111. i := 0;
  2112. WHILE i < n DO
  2113. SymTab.FieldName(t, i, fn);
  2114. ft := SymTab.FieldType(t, fn);
  2115. cls := SymTab.ClassOf(ft);
  2116. IF cls = SymTab.ClArray THEN
  2117. et := SymTab.ArrayElem(ft);
  2118. IF SymTab.ClassOf(et) = SymTab.ClArray THEN
  2119. an := SymTab.ArrayLen(ft);
  2120. k := 0;
  2121. WHILE k < an DO
  2122. Cpy(sub, recname); App(sub, "_"); App(sub, fn);
  2123. App(sub, "_");
  2124. IntStr(VAL(INTEGER, k), bv);
  2125. App(sub, bv);
  2126. ArrData(sub, et);
  2127. INC(k)
  2128. END
  2129. END
  2130. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  2131. Cpy(sub, recname); App(sub, "_"); App(sub, fn);
  2132. RecStatics(sub, ft)
  2133. END;
  2134. INC(i)
  2135. END
  2136. END RecStatics;
  2137. PROCEDURE RecItems (t: SymTab.TypeIndex; prefix: ARRAY OF CHAR;
  2138. VAR first: BOOLEAN);
  2139. (* Comma-separated item list (no wrapper); nested records inline.
  2140. Emission order follows the (prepend-built) field chain, i.e.
  2141. reverse declaration; offsets (not positions) place everything. *)
  2142. VAR n, i, k, w: CARDINAL;
  2143. fn: SymTab.Name;
  2144. ft: SymTab.TypeIndex;
  2145. cls: INTEGER;
  2146. sub: QVal;
  2147. PROCEDURE Sep;
  2148. BEGIN
  2149. IF first THEN first := FALSE ELSE W(", ") END
  2150. END Sep;
  2151. BEGIN
  2152. n := SymTab.FieldCount(t);
  2153. i := 0;
  2154. WHILE i < n DO
  2155. SymTab.FieldName(t, i, fn);
  2156. ft := SymTab.FieldType(t, fn);
  2157. cls := SymTab.ClassOf(ft);
  2158. IF cls = SymTab.ClReal THEN Sep; W("d 0")
  2159. ELSIF cls = SymTab.ClChar THEN Sep; W("b 0")
  2160. ELSIF cls = SymTab.ClArray THEN
  2161. (* array field: inline descriptor (was a pointer) *)
  2162. Sep;
  2163. Cpy(sub, prefix); App(sub, "_"); App(sub, fn);
  2164. ArrBodyItems(sub, ft)
  2165. ELSIF cls = SymTab.ClSet THEN
  2166. w := SymTab.SetWords(ft);
  2167. IF w = 0 THEN w := 1 END;
  2168. Sep; W("w 0");
  2169. k := 1;
  2170. WHILE k < w DO W(", w 0"); INC(k) END
  2171. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  2172. Cpy(sub, prefix); App(sub, "_"); App(sub, fn);
  2173. RecItems(ft, sub, first)
  2174. ELSE Sep; W("w 0")
  2175. END;
  2176. INC(i)
  2177. END
  2178. END RecItems;
  2179. PROCEDURE VtRef (t: INTEGER; VAR q: QVal);
  2180. (* The class's vtable symbol ($vt_<typeindex>). *)
  2181. VAR bv: QVal;
  2182. BEGIN
  2183. Cpy(q, "$vt_");
  2184. IntStr(t, bv);
  2185. App(q, bv)
  2186. END VtRef;
  2187. PROCEDURE EmitVTables;
  2188. (* One data array per class with a vtable: slots (inherited first) as
  2189. method code addresses. *)
  2190. VAR k, j, n: CARDINAL;
  2191. t: INTEGER;
  2192. nm: SymTab.Name;
  2193. mg, vt: QVal;
  2194. BEGIN
  2195. IF NOT opened THEN RETURN END;
  2196. k := 0;
  2197. WHILE k < SymTab.VtClassCount() DO
  2198. t := SymTab.VtClassAt(k);
  2199. n := SymTab.VtCount(t);
  2200. VtRef(t, vt);
  2201. W("data "); W(vt); W(" = { ");
  2202. j := 0;
  2203. WHILE j < n DO
  2204. IF SymTab.VtName(t, j, nm) THEN
  2205. IF j > 0 THEN W(", ") END;
  2206. IF SymTab.VtHasBody(t, j) THEN
  2207. Mangled(nm, SymTab.VtUid(t, j), mg);
  2208. W("l $"); W(mg)
  2209. ELSE
  2210. W("l 0") (* declared but never implemented *)
  2211. END
  2212. END;
  2213. INC(j)
  2214. END;
  2215. IF n = 0 THEN W("l 0") END;
  2216. WL(" }");
  2217. INC(k)
  2218. END
  2219. END EmitVTables;
  2220. PROCEDURE DeclRec (name: ARRAY OF CHAR; t: INTEGER);
  2221. VAR first: BOOLEAN;
  2222. vt, sz: QVal;
  2223. BEGIN
  2224. IF NOT opened THEN RETURN END;
  2225. IF SymTab.IsVariant(t) AND (SymTab.ClassOf(t) = SymTab.ClRecord) THEN
  2226. (* variant record: fields overlay, so emit one zero-filled blob
  2227. of the record's (max) size *)
  2228. IntStr(VAL(INTEGER, SymTab.TypeSize(t)), sz);
  2229. W("data $"); W(name); W(" = { z "); W(sz); WL(" }");
  2230. RETURN
  2231. END;
  2232. RecStatics(name, t);
  2233. W("data $"); W(name);
  2234. W(" = { ");
  2235. first := TRUE;
  2236. (* a class with a vtable stores its vtable pointer at offset 0 *)
  2237. IF (SymTab.ClassOf(t) = SymTab.ClClass) AND SymTab.HasVTable(t) THEN
  2238. VtRef(t, vt);
  2239. W("l "); W(vt);
  2240. first := FALSE
  2241. END;
  2242. RecItems(t, name, first);
  2243. IF first THEN W("w 0") END;
  2244. WL(" }")
  2245. END DeclRec;
  2246. PROCEDURE FieldAddr (base: ARRAY OF CHAR; off: INTEGER; VAR q: QVal);
  2247. VAR sb: QVal;
  2248. BEGIN
  2249. NewTemp(q);
  2250. IntStr(off, sb);
  2251. Op3L("add", q, base, sb)
  2252. END FieldAddr;
  2253. PROCEDURE ThisBase (VAR q: QVal);
  2254. (* The current method's receiver address (the hidden THIS VAR param). *)
  2255. BEGIN
  2256. AddrOf("THIS", q)
  2257. END ThisBase;
  2258. PROCEDURE PushWith (base: ARRAY OF CHAR);
  2259. BEGIN
  2260. IF withTop <= HIGH(withSt) THEN
  2261. Cpy(withSt[withTop], base); INC(withTop)
  2262. END
  2263. END PushWith;
  2264. PROCEDURE PopWith;
  2265. BEGIN
  2266. IF withTop > 0 THEN DEC(withTop) END
  2267. END PopWith;
  2268. PROCEDURE TopWith (VAR base: QVal): BOOLEAN;
  2269. BEGIN
  2270. IF withTop = 0 THEN RETURN FALSE END;
  2271. Cpy(base, withSt[withTop - 1]);
  2272. RETURN TRUE
  2273. END TopWith;
  2274. PROCEDURE CopyRecord (dst: ARRAY OF CHAR; src: ARRAY OF CHAR;
  2275. t: INTEGER);
  2276. (* Whole-record copy: a flat byte blit of the record's (max) size.
  2277. Records are flat blobs (scalars/sets/pointers inline, arrays and
  2278. nested records inline), so this is correct — and the only correct
  2279. choice for variant records, whose fields overlay. *)
  2280. VAR n: QVal;
  2281. BEGIN
  2282. IntStr(VAL(INTEGER, SymTab.TypeSize(t)), n);
  2283. Revive;
  2284. W(" call $memcpy(l "); W(dst); W(", l "); W(src);
  2285. W(", l "); W(n); WL(")")
  2286. END CopyRecord;
  2287. PROCEDURE WordsBytes (words: CARDINAL): CARDINAL;
  2288. BEGIN
  2289. IF words = 0 THEN RETURN 8 END;
  2290. RETURN ((words * 4 + 7) DIV 8) * 8
  2291. END WordsBytes;
  2292. PROCEDURE DeclSet (name: ARRAY OF CHAR; t: INTEGER);
  2293. VAR w, i: CARDINAL;
  2294. BEGIN
  2295. IF NOT opened THEN RETURN END;
  2296. w := SymTab.SetWords(t);
  2297. IF w = 0 THEN w := 1 END;
  2298. W("data $"); W(name);
  2299. W(" = { w 0");
  2300. i := 1;
  2301. WHILE i < w DO
  2302. W(", w 0");
  2303. INC(i)
  2304. END;
  2305. WL(" }")
  2306. END DeclSet;
  2307. PROCEDURE NewSetTemp (words: CARDINAL; VAR q: QVal);
  2308. VAR nb: QVal;
  2309. BEGIN
  2310. NewTemp(q);
  2311. Revive;
  2312. IntStr(VAL(INTEGER, WordsBytes(words)), nb);
  2313. W(" "); W(q); W(" =l alloc8 "); WL(nb)
  2314. END NewSetTemp;
  2315. PROCEDURE SetZero (addr: ARRAY OF CHAR; words: CARDINAL);
  2316. VAR i: CARDINAL;
  2317. a, z, off: QVal;
  2318. BEGIN
  2319. NewTemp(z);
  2320. Op3("xor", z, "0", "0", FALSE);
  2321. i := 0;
  2322. WHILE i < words DO
  2323. NewTemp(a);
  2324. IntStr(VAL(INTEGER, i * 4), off);
  2325. Op3L("add", a, addr, off);
  2326. Revive;
  2327. W(" storew "); W(z); W(", "); WL(a);
  2328. INC(i)
  2329. END
  2330. END SetZero;
  2331. PROCEDURE SetBit (addr: ARRAY OF CHAR; val: ARRAY OF CHAR;
  2332. lo: INTEGER; span: CARDINAL);
  2333. (* ORs one element in: off = val - lo trapped in [0, span). *)
  2334. VAR off, offL, hiS, loS: QVal;
  2335. wi, bi, wil, off4, wa, wcur, m, wn: QVal;
  2336. BEGIN
  2337. NewTemp(off);
  2338. IntStr(lo, loS);
  2339. Op3("sub", off, val, loS, FALSE);
  2340. WidenIndex(off, offL);
  2341. IntStr(VAL(INTEGER, span) - 1, hiS);
  2342. CheckRange(offL, "0", hiS);
  2343. NewTemp(wi);
  2344. Op3("shr", wi, off, "5", FALSE);
  2345. NewTemp(bi);
  2346. Op3("and", bi, off, "31", FALSE);
  2347. WidenIndex(wi, wil);
  2348. NewTemp(off4);
  2349. Op3L("mul", off4, wil, "4");
  2350. NewTemp(wa);
  2351. Op3L("add", wa, addr, off4);
  2352. NewTemp(m);
  2353. Op3("shl", m, "1", bi, FALSE);
  2354. NewTemp(wcur);
  2355. W(" "); W(wcur); W(" =w loadw "); WL(wa);
  2356. NewTemp(wn);
  2357. Op3("or", wn, wcur, m, FALSE);
  2358. Revive;
  2359. W(" storew "); W(wn); W(", "); WL(wa)
  2360. END SetBit;
  2361. PROCEDURE SetClearBit (addr: ARRAY OF CHAR; val: ARRAY OF CHAR;
  2362. lo: INTEGER; span: CARDINAL);
  2363. (* ANDs one element out: off = val - lo trapped in [0, span). *)
  2364. VAR off, offL, hiS, loS: QVal;
  2365. wi, bi, wil, off4, wa, wcur, m, nm, wn: QVal;
  2366. BEGIN
  2367. NewTemp(off);
  2368. IntStr(lo, loS);
  2369. Op3("sub", off, val, loS, FALSE);
  2370. WidenIndex(off, offL);
  2371. IntStr(VAL(INTEGER, span) - 1, hiS);
  2372. CheckRange(offL, "0", hiS);
  2373. NewTemp(wi);
  2374. Op3("shr", wi, off, "5", FALSE);
  2375. NewTemp(bi);
  2376. Op3("and", bi, off, "31", FALSE);
  2377. WidenIndex(wi, wil);
  2378. NewTemp(off4);
  2379. Op3L("mul", off4, wil, "4");
  2380. NewTemp(wa);
  2381. Op3L("add", wa, addr, off4);
  2382. NewTemp(m);
  2383. Op3("shl", m, "1", bi, FALSE);
  2384. NewTemp(nm);
  2385. Op3("xor", nm, m, "-1", FALSE);
  2386. NewTemp(wcur);
  2387. W(" "); W(wcur); W(" =w loadw "); WL(wa);
  2388. NewTemp(wn);
  2389. Op3("and", wn, wcur, nm, FALSE);
  2390. Revive;
  2391. W(" storew "); W(wn); W(", "); WL(wa)
  2392. END SetClearBit;
  2393. PROCEDURE SetShift (src: ARRAY OF CHAR; n: ARRAY OF CHAR;
  2394. words: CARDINAL; span: CARDINAL; isRot: BOOLEAN;
  2395. VAR q: QVal);
  2396. (* Bit-by-bit shift/rotate by a runtime amount n over [0, span);
  2397. bits that leave the span are dropped (SHIFT) or wrapped (ROTATE). *)
  2398. VAR out, nn, sp, hi, i: QVal;
  2399. wi, bi, wil, off4, wa, m, t, bit, nzb: QVal;
  2400. j, c, cs, jw: QVal;
  2401. ok1, ok2, ok, wj, bj, wjl, o2, wb2, mm, mm2, mm3: QVal;
  2402. wcur, wn, ip1: QVal;
  2403. ltop, lbody, lend: QVal;
  2404. BEGIN
  2405. NewSetTemp(words, out);
  2406. SetZero(out, words);
  2407. Cpy(nn, n);
  2408. IntStr(VAL(INTEGER, span), sp);
  2409. IntStr(VAL(INTEGER, span) - 1, hi);
  2410. IF isRot THEN
  2411. NewTemp(j); Op3("rem", j, n, sp, FALSE);
  2412. NewTemp(ok1); Op3("add", ok1, j, sp, FALSE);
  2413. NewTemp(cs); Op3("rem", cs, ok1, sp, FALSE);
  2414. Cpy(nn, cs)
  2415. END;
  2416. NewTemp(i); Op3("add", i, "0", "0", FALSE);
  2417. NewLabel(ltop); NewLabel(lbody); NewLabel(lend);
  2418. EmitLabel(ltop);
  2419. NewTemp(c); Op3("cslew", c, i, hi, FALSE);
  2420. Jnz(c, lbody, lend);
  2421. EmitLabel(lbody);
  2422. NewTemp(wi); Op3("shr", wi, i, "5", FALSE);
  2423. NewTemp(bi); Op3("and", bi, i, "31", FALSE);
  2424. WidenIndex(wi, wil);
  2425. NewTemp(off4); Op3L("mul", off4, wil, "4");
  2426. NewTemp(wa); Op3L("add", wa, src, off4);
  2427. NewTemp(m); Op3("shl", m, "1", bi, FALSE);
  2428. NewTemp(t); W(" "); W(t); W(" =w loadw "); WL(wa);
  2429. NewTemp(bit); Op3("and", bit, t, m, FALSE);
  2430. NewTemp(nzb); Op3("cnew", nzb, bit, "0", FALSE);
  2431. NewTemp(j); Op3("add", j, i, nn, FALSE);
  2432. IF isRot THEN
  2433. NewTemp(c); Op3("csgew", c, j, sp, FALSE);
  2434. NewTemp(cs); Op3("mul", cs, c, sp, FALSE);
  2435. NewTemp(jw); Op3("sub", jw, j, cs, FALSE);
  2436. Cpy(j, jw)
  2437. END;
  2438. NewTemp(ok1); Op3("csgew", ok1, j, "0", FALSE);
  2439. NewTemp(ok2); Op3("cslew", ok2, j, hi, FALSE);
  2440. NewTemp(ok); Op3("and", ok, ok1, ok2, FALSE);
  2441. NewTemp(wj); Op3("shr", wj, j, "5", FALSE);
  2442. NewTemp(bj); Op3("and", bj, j, "31", FALSE);
  2443. WidenIndex(wj, wjl);
  2444. NewTemp(o2); Op3L("mul", o2, wjl, "4");
  2445. NewTemp(wb2); Op3L("add", wb2, out, o2);
  2446. NewTemp(mm); Op3("shl", mm, "1", bj, FALSE);
  2447. NewTemp(mm2); Op3("mul", mm2, mm, nzb, FALSE);
  2448. NewTemp(mm3); Op3("mul", mm3, mm2, ok, FALSE);
  2449. NewTemp(wcur); W(" "); W(wcur); W(" =w loadw "); WL(wb2);
  2450. NewTemp(wn); Op3("or", wn, wcur, mm3, FALSE);
  2451. Revive;
  2452. W(" storew "); W(wn); W(", "); WL(wb2);
  2453. Op3("add", i, i, "1", FALSE);
  2454. Jmp(ltop);
  2455. EmitLabel(lend);
  2456. Cpy(q, out)
  2457. END SetShift;
  2458. PROCEDURE SetRange (addr: ARRAY OF CHAR; a: ARRAY OF CHAR;
  2459. b: ARRAY OF CHAR; lo: INTEGER; span: CARDINAL);
  2460. VAR al, bl, hiS: QVal;
  2461. cur, c: QVal;
  2462. ltop, lbody, lend: QVal;
  2463. BEGIN
  2464. WidenIndex(a, al);
  2465. WidenIndex(b, bl);
  2466. IntStr(VAL(INTEGER, span) - 1, hiS);
  2467. CheckRange(al, "0", hiS);
  2468. CheckRange(bl, "0", hiS);
  2469. NewTemp(cur);
  2470. Op3("add", cur, a, "0", FALSE);
  2471. NewLabel(ltop); NewLabel(lbody); NewLabel(lend);
  2472. EmitLabel(ltop);
  2473. NewTemp(c);
  2474. Op3("cslew", c, cur, b, FALSE);
  2475. Jnz(c, lbody, lend);
  2476. EmitLabel(lbody);
  2477. SetBit(addr, cur, lo, span);
  2478. Op3("add", cur, cur, "1", FALSE);
  2479. Jmp(ltop);
  2480. EmitLabel(lend)
  2481. END SetRange;
  2482. PROCEDURE SetBinOp (sel: INTEGER; l: ARRAY OF CHAR; r: ARRAY OF CHAR;
  2483. lw, rw: CARDINAL; VAR q: QVal);
  2484. (* Union/intersection/difference/symdiff over possibly different
  2485. spans: overlap via op, larger-side extras copied (union/symdiff/
  2486. left-diff) or zeroed. Result has max words. *)
  2487. VAR i, m, res: CARDINAL;
  2488. la, ra, ta, a, b, c, nb, z: QVal;
  2489. BEGIN
  2490. m := lw;
  2491. IF rw < m THEN m := rw END;
  2492. res := lw;
  2493. IF rw > res THEN res := rw END;
  2494. NewSetTemp(res, q);
  2495. NewTemp(z);
  2496. Op3("xor", z, "0", "0", FALSE);
  2497. i := 0;
  2498. WHILE i < m DO
  2499. IntStr(VAL(INTEGER, i * 4), nb);
  2500. NewTemp(la); Op3L("add", la, l, nb);
  2501. NewTemp(ra); Op3L("add", ra, r, nb);
  2502. NewTemp(ta); Op3L("add", ta, q, nb);
  2503. NewTemp(a);
  2504. W(" "); W(a); W(" =w loadw "); WL(la);
  2505. NewTemp(b);
  2506. W(" "); W(b); W(" =w loadw "); WL(ra);
  2507. NewTemp(c);
  2508. IF sel = 0 THEN Op3("or", c, a, b, FALSE)
  2509. ELSIF sel = 1 THEN Op3("and", c, a, b, FALSE)
  2510. ELSIF sel = 2 THEN
  2511. NewTemp(nb);
  2512. Op3("xor", nb, b, "-1", FALSE);
  2513. Op3("and", c, a, nb, FALSE)
  2514. ELSE Op3("xor", c, a, b, FALSE)
  2515. END;
  2516. Revive;
  2517. W(" storew "); W(c); W(", "); WL(ta);
  2518. INC(i)
  2519. END;
  2520. WHILE i < lw DO
  2521. IntStr(VAL(INTEGER, i * 4), nb);
  2522. NewTemp(la); Op3L("add", la, l, nb);
  2523. NewTemp(ta); Op3L("add", ta, q, nb);
  2524. IF (sel = 0) OR (sel = 2) OR (sel = 3) THEN
  2525. NewTemp(a);
  2526. W(" "); W(a); W(" =w loadw "); WL(la);
  2527. Revive;
  2528. W(" storew "); W(a); W(", "); WL(ta)
  2529. ELSE
  2530. Revive;
  2531. W(" storew "); W(z); W(", "); WL(ta)
  2532. END;
  2533. INC(i)
  2534. END;
  2535. WHILE i < rw DO
  2536. IntStr(VAL(INTEGER, i * 4), nb);
  2537. NewTemp(ra); Op3L("add", ra, r, nb);
  2538. NewTemp(ta); Op3L("add", ta, q, nb);
  2539. IF (sel = 0) OR (sel = 3) THEN
  2540. NewTemp(b);
  2541. W(" "); W(b); W(" =w loadw "); WL(ra);
  2542. Revive;
  2543. W(" storew "); W(b); W(", "); WL(ta)
  2544. ELSE
  2545. Revive;
  2546. W(" storew "); W(z); W(", "); WL(ta)
  2547. END;
  2548. INC(i)
  2549. END
  2550. END SetBinOp;
  2551. PROCEDURE CmpSet (op: INTEGER; l: ARRAY OF CHAR; r: ARRAY OF CHAR;
  2552. lw, rw: CARDINAL; VAR q: QVal);
  2553. VAR i, m: CARDINAL;
  2554. la, ra, a, b, c, acc, nb: QVal;
  2555. BEGIN
  2556. NewTemp(acc);
  2557. Op3("xor", acc, "1", "0", FALSE);
  2558. m := lw;
  2559. IF rw < m THEN m := rw END;
  2560. i := 0;
  2561. WHILE i < m DO
  2562. IntStr(VAL(INTEGER, i * 4), nb);
  2563. NewTemp(la); Op3L("add", la, l, nb);
  2564. NewTemp(ra); Op3L("add", ra, r, nb);
  2565. NewTemp(a);
  2566. W(" "); W(a); W(" =w loadw "); WL(la);
  2567. NewTemp(b);
  2568. W(" "); W(b); W(" =w loadw "); WL(ra);
  2569. NewTemp(c);
  2570. Op3("ceqw", c, a, b, FALSE);
  2571. NewTemp(q);
  2572. Op3("and", q, acc, c, FALSE);
  2573. CopyOp(q, acc);
  2574. INC(i)
  2575. END;
  2576. WHILE i < lw DO
  2577. IntStr(VAL(INTEGER, i * 4), nb);
  2578. NewTemp(la); Op3L("add", la, l, nb);
  2579. NewTemp(a);
  2580. W(" "); W(a); W(" =w loadw "); WL(la);
  2581. NewTemp(c);
  2582. Op3("ceqw", c, a, "0", FALSE);
  2583. NewTemp(q);
  2584. Op3("and", q, acc, c, FALSE);
  2585. CopyOp(q, acc);
  2586. INC(i)
  2587. END;
  2588. WHILE i < rw DO
  2589. IntStr(VAL(INTEGER, i * 4), nb);
  2590. NewTemp(ra); Op3L("add", ra, r, nb);
  2591. NewTemp(b);
  2592. W(" "); W(b); W(" =w loadw "); WL(ra);
  2593. NewTemp(c);
  2594. Op3("ceqw", c, b, "0", FALSE);
  2595. NewTemp(q);
  2596. Op3("and", q, acc, c, FALSE);
  2597. CopyOp(q, acc);
  2598. INC(i)
  2599. END;
  2600. IF (op = SymTab.OpNeq1) OR (op = SymTab.OpNeq2) THEN
  2601. NewTemp(q);
  2602. Op3("xor", q, acc, "1", FALSE)
  2603. ELSE
  2604. CopyOp(acc, q)
  2605. END
  2606. END CmpSet;
  2607. PROCEDURE CopySet (dst: ARRAY OF CHAR; src: ARRAY OF CHAR;
  2608. dw, sw: CARDINAL);
  2609. (* Copies min words via memcpy, zero-fills dst extras. Src extras
  2610. beyond dst must be zero (trap) — otherwise out-of-span bits
  2611. would vanish silently on narrowing assignment. *)
  2612. VAR m, i: CARDINAL;
  2613. n, da, sa, a, z, off, c: QVal;
  2614. qr, acc, lok, lbad: QVal;
  2615. BEGIN
  2616. m := dw;
  2617. IF sw < m THEN m := sw END;
  2618. IF sw > dw THEN
  2619. NewTemp(acc);
  2620. Op3("xor", acc, "1", "0", FALSE);
  2621. i := dw;
  2622. WHILE i < sw DO
  2623. IntStr(VAL(INTEGER, i * 4), off);
  2624. NewTemp(a); Op3L("add", a, src, off);
  2625. NewTemp(c);
  2626. W(" "); W(c); W(" =w loadw "); WL(a);
  2627. NewTemp(qr);
  2628. Op3("ceqw", qr, c, "0", FALSE);
  2629. NewTemp(c);
  2630. Op3("and", c, acc, qr, FALSE);
  2631. CopyOp(c, acc);
  2632. INC(i)
  2633. END;
  2634. NewLabel(lok); NewLabel(lbad);
  2635. Jnz(acc, lok, lbad);
  2636. EmitLabel(lbad);
  2637. Trap;
  2638. EmitLabel(lok)
  2639. END;
  2640. IF m > 0 THEN
  2641. IntStr(VAL(INTEGER, m * 4), n);
  2642. NewTemp(qr);
  2643. W(" "); W(qr); W(" =l call $memcpy(l ");
  2644. W(dst); W(", l "); W(src); W(", l "); W(n); WL(")")
  2645. END;
  2646. NewTemp(z);
  2647. Op3("xor", z, "0", "0", FALSE);
  2648. i := m;
  2649. WHILE i < dw DO
  2650. NewTemp(a);
  2651. IntStr(VAL(INTEGER, i * 4), off);
  2652. Op3L("add", a, dst, off);
  2653. Revive;
  2654. W(" storew "); W(z); W(", "); WL(a);
  2655. INC(i)
  2656. END
  2657. END CopySet;
  2658. PROCEDURE InSet (x: ARRAY OF CHAR; s: ARRAY OF CHAR; lo: INTEGER;
  2659. span: CARDINAL; VAR q: QVal);
  2660. (* Membership bit test with span trap; q is fresh w 0/1. *)
  2661. VAR off, offL, hiS, loS: QVal;
  2662. wi, bi, wil, off4, wa, wcur, m, a: QVal;
  2663. spanS: QVal;
  2664. BEGIN
  2665. NewTemp(off);
  2666. IntStr(lo, loS);
  2667. Op3("sub", off, x, loS, FALSE);
  2668. WidenIndex(off, offL);
  2669. IntStr(VAL(INTEGER, span) - 1, spanS);
  2670. CheckRange(offL, "0", spanS);
  2671. NewTemp(wi);
  2672. Op3("shr", wi, off, "5", FALSE);
  2673. NewTemp(bi);
  2674. Op3("and", bi, off, "31", FALSE);
  2675. WidenIndex(wi, wil);
  2676. NewTemp(off4);
  2677. Op3L("mul", off4, wil, "4");
  2678. NewTemp(wa);
  2679. Op3L("add", wa, s, off4);
  2680. NewTemp(wcur);
  2681. W(" "); W(wcur); W(" =w loadw "); WL(wa);
  2682. NewTemp(m);
  2683. Op3("shl", m, "1", bi, FALSE);
  2684. NewTemp(a);
  2685. Op3("and", a, wcur, m, FALSE);
  2686. NewTemp(q);
  2687. Op3("cnew", q, a, "0", FALSE)
  2688. END InSet;
  2689. PROCEDURE WidenIndex (idx: ARRAY OF CHAR; VAR q: QVal);
  2690. BEGIN
  2691. IF IsImm(idx) THEN Cpy(q, idx)
  2692. ELSE
  2693. NewTemp(q);
  2694. Revive;
  2695. W(" "); W(q); W(" =l extsw "); WL(idx)
  2696. END
  2697. END WidenIndex;
  2698. PROCEDURE OpenHi (base: ARRAY OF CHAR; VAR q: QVal);
  2699. VAR c: QVal;
  2700. BEGIN
  2701. NewTemp(c);
  2702. Revive;
  2703. W(" "); W(c); W(" =l loadl "); WL(base);
  2704. NewTemp(q);
  2705. Op3L("sub", q, c, "1")
  2706. END OpenHi;
  2707. PROCEDURE OpenHiChar (base: ARRAY OF CHAR; VAR q: QVal);
  2708. (* q := loadl(base) — the open-array count, i.e. the index of the NUL
  2709. terminator slot that CHAR arrays reserve. *)
  2710. BEGIN
  2711. NewTemp(q);
  2712. Revive;
  2713. W(" "); W(q); W(" =l loadl "); WL(base)
  2714. END OpenHiChar;
  2715. PROCEDURE LoadCount (base: ARRAY OF CHAR; VAR q: QVal);
  2716. BEGIN
  2717. NewTemp(q);
  2718. Revive;
  2719. W(" "); W(q); W(" =l loadl "); WL(base)
  2720. END LoadCount;
  2721. PROCEDURE CheckRange (idx, lo, hi: ARRAY OF CHAR);
  2722. (* l-domain operands (widened index, immediates, or open hi temp);
  2723. comparisons are long (result w). *)
  2724. VAR c1, c2, c: QVal;
  2725. lok, lbad: QVal;
  2726. BEGIN
  2727. NewTemp(c1);
  2728. Op3("csgel", c1, idx, lo, FALSE);
  2729. NewTemp(c2);
  2730. Op3("cslel", c2, idx, hi, FALSE);
  2731. NewTemp(c);
  2732. Op3("and", c, c1, c2, FALSE);
  2733. NewLabel(lok); NewLabel(lbad);
  2734. Jnz(c, lok, lbad);
  2735. EmitLabel(lbad);
  2736. Trap;
  2737. EmitLabel(lok)
  2738. END CheckRange;
  2739. PROCEDURE ElemAddr (base, idx, lo: ARRAY OF CHAR; elemT: INTEGER;
  2740. VAR q: QVal);
  2741. (* q := base + 8 + (idx - lo) * elemSize in l, fresh temp.
  2742. elemT is the array descriptor (open or fixed); all operands
  2743. l-domain (immediates pass, temps pre-widened). *)
  2744. VAR t1, t2, tb, sb: QVal;
  2745. BEGIN
  2746. NewTemp(t1);
  2747. Op3L("sub", t1, idx, lo);
  2748. IntStr(VAL(INTEGER, ElemSize(elemT)), sb);
  2749. NewTemp(t2);
  2750. Op3L("mul", t2, t1, sb);
  2751. NewTemp(tb);
  2752. Op3L("add", tb, base, "8");
  2753. NewTemp(q);
  2754. Op3L("add", q, tb, t2)
  2755. END ElemAddr;
  2756. PROCEDURE ElemLoad (addr: ARRAY OF CHAR; t: INTEGER; VAR q: QVal);
  2757. (* Loads one element of (element-)type t. Nested/pointer elements
  2758. are addresses (loadl). *)
  2759. VAR cls: INTEGER;
  2760. BEGIN
  2761. cls := SymTab.ClassOf(t);
  2762. NewTemp(q);
  2763. Revive;
  2764. W(" "); W(q);
  2765. IF cls = SymTab.ClReal THEN W(" =d loadd ")
  2766. ELSIF (cls = SymTab.ClArray) OR (cls = SymTab.ClPtr)
  2767. OR (cls = SymTab.ClLong) OR (cls = SymTab.ClProc)
  2768. OR (cls = SymTab.ClClass) THEN
  2769. W(" =l loadl ")
  2770. ELSIF cls = SymTab.ClChar THEN W(" =w loadub ")
  2771. ELSE W(" =w loadw ")
  2772. END;
  2773. WL(addr)
  2774. END ElemLoad;
  2775. PROCEDURE ElemStore (addr, v: ARRAY OF CHAR; t: INTEGER);
  2776. VAR cls: INTEGER;
  2777. BEGIN
  2778. cls := SymTab.ClassOf(t);
  2779. Revive;
  2780. IF cls = SymTab.ClReal THEN W(" stored ")
  2781. ELSIF (cls = SymTab.ClArray) OR (cls = SymTab.ClPtr)
  2782. OR (cls = SymTab.ClLong) OR (cls = SymTab.ClProc)
  2783. OR (cls = SymTab.ClClass) THEN
  2784. W(" storel ")
  2785. ELSIF cls = SymTab.ClChar THEN W(" storeb ")
  2786. ELSE W(" storew ")
  2787. END;
  2788. W(v); W(", "); WL(addr)
  2789. END ElemStore;
  2790. PROCEDURE CopyArray (dst: ARRAY OF CHAR; src: ARRAY OF CHAR;
  2791. t: INTEGER);
  2792. (* Whole-array copy with runtime count check + blit. Nested levels
  2793. recurse through a runtime loop; counts mismatch traps. dst/src
  2794. are descriptor address operands; t is the destination type. *)
  2795. VAR dc, sc, ok: QVal;
  2796. lok, lbad: QVal;
  2797. ecls: INTEGER;
  2798. n, da, sa: QVal;
  2799. sb: QVal;
  2800. qr: QVal;
  2801. i, c, de, se, di, si, dd0, doff, sd0, soff: QVal;
  2802. ltop, lbody, lend: QVal;
  2803. BEGIN
  2804. NewTemp(dc);
  2805. Revive;
  2806. W(" "); W(dc); W(" =l loadl "); WL(dst);
  2807. NewTemp(sc);
  2808. W(" "); W(sc); W(" =l loadl "); WL(src);
  2809. NewTemp(ok);
  2810. Op3("ceql", ok, dc, sc, FALSE);
  2811. NewLabel(lok); NewLabel(lbad);
  2812. Jnz(ok, lok, lbad);
  2813. EmitLabel(lbad);
  2814. Trap;
  2815. EmitLabel(lok);
  2816. ecls := ElemCls(t);
  2817. IF (ecls = SymTab.ClArray) THEN
  2818. NewTemp(i);
  2819. W(" "); W(i); W(" =l copy 0"); WL("");
  2820. NewLabel(ltop); NewLabel(lbody); NewLabel(lend);
  2821. EmitLabel(ltop);
  2822. NewTemp(c);
  2823. Op3("csltl", c, i, dc, FALSE);
  2824. Jnz(c, lbody, lend);
  2825. EmitLabel(lbody);
  2826. NewTemp(dd0); Op3L("add", dd0, dst, "8");
  2827. NewTemp(doff); Op3L("mul", doff, i, "8");
  2828. NewTemp(de); Op3L("add", de, dd0, doff);
  2829. NewTemp(sd0); Op3L("add", sd0, src, "8");
  2830. NewTemp(soff); Op3L("mul", soff, i, "8");
  2831. NewTemp(se); Op3L("add", se, sd0, soff);
  2832. NewTemp(di);
  2833. W(" "); W(di); W(" =l loadl "); WL(de);
  2834. NewTemp(si);
  2835. W(" "); W(si); W(" =l loadl "); WL(se);
  2836. CopyArray(di, si, SymTab.ArrayElem(t));
  2837. Op3L("add", i, i, "1");
  2838. Jmp(ltop);
  2839. EmitLabel(lend)
  2840. ELSE
  2841. IntStr(VAL(INTEGER, ElemSize(t)), sb);
  2842. NewTemp(n);
  2843. Op3L("mul", n, dc, sb);
  2844. NewTemp(da);
  2845. Op3L("add", da, dst, "8");
  2846. NewTemp(sa);
  2847. Op3L("add", sa, src, "8");
  2848. NewTemp(qr);
  2849. W(" "); W(qr); W(" =l call $memcpy(l ");
  2850. W(da); W(", l "); W(sa); W(", l "); W(n); WL(")")
  2851. END
  2852. END CopyArray;
  2853. PROCEDURE DeclStr (text: ARRAY OF CHAR; VAR q: QVal);
  2854. (* Records a quoted literal for top-level emission at EndModule and
  2855. returns its address operand ($strN). Data definitions may only
  2856. appear outside functions, but literals occur mid-body. *)
  2857. VAR nm: QVal;
  2858. BEGIN
  2859. Cpy(nm, "str");
  2860. AppNum(nm, nStr);
  2861. IF nStr <= HIGH(strNams) THEN
  2862. Cpy(strNams[nStr], nm);
  2863. Cpy(strTexts[nStr], text);
  2864. INC(nStr)
  2865. END;
  2866. Cpy(q, "$");
  2867. App(q, nm)
  2868. END DeclStr;
  2869. PROCEDURE DeclUStr (text: ARRAY OF CHAR; VAR q: QVal;
  2870. VAR single: BOOLEAN; VAR cp: INTEGER;
  2871. VAR ok: BOOLEAN);
  2872. (* text is the raw lexeme U'...' / U"..." Strict RFC3629-decode the
  2873. bytes between the quotes. Exactly one codepoint -> single:=TRUE,
  2874. cp:=it. Otherwise record a UString descriptor and q := "$ustrN".
  2875. ok:=FALSE for invalid UTF-8 (overlong / surrogate / >10FFFF /
  2876. truncation). *)
  2877. VAR L, i, cnt, base: CARDINAL;
  2878. b0, b1, b2, b3, ch: INTEGER;
  2879. nm: QVal;
  2880. PROCEDURE Cont (VAR b: INTEGER): BOOLEAN;
  2881. (* consume a continuation byte, or fail *)
  2882. BEGIN
  2883. IF i >= L - 1 THEN RETURN FALSE END;
  2884. b := ORD(text[i]); INC(i);
  2885. RETURN (b >= 128) AND (b <= 191)
  2886. END Cont;
  2887. BEGIN
  2888. single := FALSE; cp := 0; ok := TRUE;
  2889. q[0] := "0"; q[1] := CHR(0);
  2890. L := Len(text);
  2891. IF (L < 3) OR (text[0] # "U") THEN ok := FALSE; RETURN END;
  2892. i := 2; cnt := 0; base := ustrUsed;
  2893. WHILE i < L - 1 DO
  2894. b0 := ORD(text[i]); INC(i);
  2895. ch := -1;
  2896. IF b0 <= 127 THEN
  2897. ch := b0
  2898. ELSIF (b0 >= 194) AND (b0 <= 223) THEN
  2899. IF Cont(b1) THEN ch := ((b0 - 192) * 64) + (b1 - 128) END
  2900. ELSIF (b0 >= 224) AND (b0 <= 239) THEN
  2901. IF Cont(b1) AND Cont(b2) THEN
  2902. ch := ((b0 - 224) * 4096) + ((b1 - 128) * 64) + (b2 - 128);
  2903. IF ch < 2048 THEN ch := -1 END
  2904. END
  2905. ELSIF (b0 >= 240) AND (b0 <= 244) THEN
  2906. IF Cont(b1) AND Cont(b2) AND Cont(b3) THEN
  2907. ch := ((b0 - 240) * 262144) + ((b1 - 128) * 4096)
  2908. + ((b2 - 128) * 64) + (b3 - 128);
  2909. IF ch < 65536 THEN ch := -1 END
  2910. END
  2911. ELSE ok := FALSE
  2912. END;
  2913. IF ok AND (ch < 0) THEN ok := FALSE END;
  2914. IF ok AND (ch >= 55296) AND (ch <= 57343) THEN ok := FALSE END;
  2915. IF ok AND (ch > 1114111) THEN ok := FALSE END;
  2916. IF NOT ok THEN RETURN END;
  2917. IF ustrUsed <= HIGH(ustrPool) THEN
  2918. ustrPool[ustrUsed] := ch; INC(ustrUsed)
  2919. END;
  2920. INC(cnt)
  2921. END;
  2922. IF cnt = 1 THEN
  2923. single := TRUE; cp := ustrPool[base]
  2924. ELSE
  2925. Cpy(nm, "ustr"); AppNum(nm, ustrN);
  2926. IF ustrN <= HIGH(ustrNams) THEN
  2927. Cpy(ustrNams[ustrN], nm);
  2928. ustrStart[ustrN] := base; ustrCount[ustrN] := cnt; INC(ustrN)
  2929. END;
  2930. Cpy(q, "$"); App(q, nm)
  2931. END
  2932. END DeclUStr;
  2933. PROCEDURE FlushUStrings;
  2934. VAR k, i: CARDINAL;
  2935. bv: QVal;
  2936. BEGIN
  2937. IF NOT opened THEN RETURN END;
  2938. k := 0;
  2939. WHILE k < ustrN DO
  2940. W("data $"); W(ustrNams[k]); W(" = { l ");
  2941. IntStr(VAL(INTEGER, ustrCount[k]), bv); W(bv);
  2942. i := 0;
  2943. WHILE i < ustrCount[k] DO
  2944. W(", w ");
  2945. IntStr(ustrPool[ustrStart[k] + i], bv); W(bv);
  2946. INC(i)
  2947. END;
  2948. WL(" }");
  2949. INC(k)
  2950. END
  2951. END FlushUStrings;
  2952. PROCEDURE DeclCharStr (ch: ARRAY OF CHAR; VAR q: QVal);
  2953. VAR v: INTEGER;
  2954. txt: ARRAY [0 .. 3] OF CHAR;
  2955. BEGIN
  2956. IF ParseInt(ch, v) AND (v >= 0) AND (v < 256) THEN
  2957. txt[0] := '"'; txt[1] := CHR(v); txt[2] := '"'; txt[3] := CHR(0);
  2958. DeclStr(txt, q)
  2959. ELSE Cpy(q, "0")
  2960. END
  2961. END DeclCharStr;
  2962. PROCEDURE FindStrPos (v: ARRAY OF CHAR; VAR k: CARDINAL): BOOLEAN;
  2963. VAR j: CARDINAL;
  2964. sv: QVal;
  2965. BEGIN
  2966. j := 0;
  2967. WHILE j < nStr DO
  2968. Cpy(sv, "$"); App(sv, strNams[j]);
  2969. IF SymTab.Equal(sv, v) THEN k := j; RETURN TRUE END;
  2970. INC(j)
  2971. END;
  2972. RETURN FALSE
  2973. END FindStrPos;
  2974. PROCEDURE StrFold (a, b: ARRAY OF CHAR; clsA, clsB: INTEGER;
  2975. VAR q: QVal; VAR ok: BOOLEAN);
  2976. (* Constant-fold `a + b` into one string literal descriptor. *)
  2977. VAR ka, kb: CARDINAL;
  2978. ta, tb, joined: ARRAY [0 .. 1023] OF CHAR;
  2979. oa, ob, ord: INTEGER;
  2980. i, L: CARDINAL;
  2981. isCharA, isCharB: BOOLEAN;
  2982. PROCEDURE AppendInner (VAR s: ARRAY OF CHAR; txt: ARRAY OF CHAR);
  2983. VAR n: CARDINAL;
  2984. c2: ARRAY [0 .. 1] OF CHAR;
  2985. BEGIN
  2986. n := Len(txt);
  2987. IF n >= 2 THEN
  2988. i := 1;
  2989. WHILE i < n - 1 DO
  2990. c2[0] := txt[i]; c2[1] := CHR(0);
  2991. App(s, c2);
  2992. INC(i)
  2993. END
  2994. END
  2995. END AppendInner;
  2996. BEGIN
  2997. ok := FALSE;
  2998. isCharA := (clsA = SymTab.ClChar) OR (clsA = SymTab.ClEnum)
  2999. OR (clsA = SymTab.ClBool);
  3000. isCharB := (clsB = SymTab.ClChar) OR (clsB = SymTab.ClEnum)
  3001. OR (clsB = SymTab.ClBool);
  3002. IF isCharA THEN
  3003. IF NOT ParseInt(a, oa) THEN RETURN END
  3004. ELSIF NOT FindStrPos(a, ka) THEN RETURN
  3005. END;
  3006. IF isCharB THEN
  3007. IF NOT ParseInt(b, ob) THEN RETURN END
  3008. ELSIF NOT FindStrPos(b, kb) THEN RETURN
  3009. END;
  3010. joined[0] := '"'; joined[1] := CHR(0);
  3011. IF isCharA THEN
  3012. IF (oa < 0) OR (oa > 255) THEN RETURN END;
  3013. joined[1] := CHR(oa); joined[2] := CHR(0)
  3014. ELSE AppendInner(joined, strTexts[ka])
  3015. END;
  3016. IF isCharB THEN
  3017. IF (ob < 0) OR (ob > 255) THEN RETURN END;
  3018. L := Len(joined); joined[L] := CHR(ob); joined[L + 1] := CHR(0)
  3019. ELSE AppendInner(joined, strTexts[kb])
  3020. END;
  3021. L := Len(joined); joined[L] := '"'; joined[L + 1] := CHR(0);
  3022. DeclStr(joined, q);
  3023. ok := TRUE
  3024. END StrFold;
  3025. PROCEDURE FlushStrings;
  3026. (* Emits all recorded string literals as top-level data. *)
  3027. VAR k, i, L: CARDINAL;
  3028. bv: QVal;
  3029. BEGIN
  3030. IF NOT opened THEN RETURN END;
  3031. k := 0;
  3032. WHILE k < nStr DO
  3033. L := Len(strTexts[k]);
  3034. IF L >= 2 THEN
  3035. W("data $"); W(strNams[k]);
  3036. W(" = { l ");
  3037. IntStr(VAL(INTEGER, L - 2), bv);
  3038. W(bv);
  3039. i := 1;
  3040. WHILE i < L - 1 DO
  3041. W(", b ");
  3042. IntStr(ORD(strTexts[k][i]), bv);
  3043. W(bv);
  3044. INC(i)
  3045. END;
  3046. W(", b 0"); (* NUL terminator (count excludes it) *)
  3047. WL(" }")
  3048. END;
  3049. INC(k)
  3050. END
  3051. END FlushStrings;
  3052. PROCEDURE DeclArr (name: ARRAY OF CHAR; t: INTEGER);
  3053. BEGIN
  3054. IF SymTab.IsOpenArray(t) OR (SymTab.ArrayDepth(t) = 0) THEN
  3055. DataLine(name, FALSE, "0"); RETURN
  3056. END;
  3057. ArrData(name, t)
  3058. END DeclArr;
  3059. (* ---------------- array constructors (step: `T{...}`) ---------------- *)
  3060. (* A constructor becomes a pooled static descriptor with the same
  3061. layout as a declared array: "l <ArrayLen>, <items>" (CHAR/UCHAR
  3062. get a trailing zero terminator slot, like ArrBodyItems). Nested
  3063. arrays are emitted as their own pooled descriptors and referenced
  3064. by `l $ctoK` so the runtime descriptor pointers stay valid. *)
  3065. PROCEDURE IsBakeOp (v: ARRAY OF CHAR): BOOLEAN;
  3066. (* TRUE when v may be embedded in a static descriptor: an immediate,
  3067. or a pooled constructor/label reference ($ctoN). A plain global
  3068. address ($mod_var) is NOT bakeable into an inline field. *)
  3069. BEGIN
  3070. IF (v[0] = CHR(0)) OR (v[0] = "%") THEN RETURN FALSE END;
  3071. IF v[0] # "$" THEN RETURN TRUE END;
  3072. (* accept $ctoN only *)
  3073. RETURN (v[1] = "c") AND (v[2] = "t") AND (v[3] = "o")
  3074. END IsBakeOp;
  3075. PROCEDURE CtorItem (VAR buf: ARRAY OF CHAR; v: ARRAY OF CHAR;
  3076. cls: INTEGER);
  3077. BEGIN
  3078. App(buf, ", ");
  3079. IF cls = SymTab.ClChar THEN
  3080. App(buf, "b ")
  3081. ELSIF cls = SymTab.ClReal THEN
  3082. App(buf, "d ")
  3083. ELSIF (cls = SymTab.ClPtr) OR (cls = SymTab.ClProc)
  3084. OR (cls = SymTab.ClLong) OR (cls = SymTab.ClArray) THEN
  3085. App(buf, "l ")
  3086. ELSE
  3087. App(buf, "w ")
  3088. END;
  3089. App(buf, v)
  3090. END CtorItem;
  3091. PROCEDURE RecItem (VAR s: ARRAY OF CHAR; VAR first: BOOLEAN;
  3092. v: ARRAY OF CHAR; cls: INTEGER);
  3093. (* Record field item with a leading separator when not first. *)
  3094. BEGIN
  3095. IF first THEN first := FALSE ELSE App(s, ", ") END;
  3096. IF cls = SymTab.ClChar THEN App(s, "b ")
  3097. ELSIF cls = SymTab.ClReal THEN App(s, "d ")
  3098. ELSIF (cls = SymTab.ClPtr) OR (cls = SymTab.ClProc)
  3099. OR (cls = SymTab.ClLong) OR (cls = SymTab.ClArray) THEN
  3100. App(s, "l ")
  3101. ELSE
  3102. App(s, "w ")
  3103. END;
  3104. App(s, v)
  3105. END RecItem;
  3106. PROCEDURE ArrZeroBody (VAR s: ARRAY OF CHAR; t: INTEGER);
  3107. (* "l <n>, z <bytes>" — an all-zero array body (used for a record's
  3108. unprovided array field). *)
  3109. VAR n: CARDINAL;
  3110. elem: SymTab.TypeIndex;
  3111. ecls: INTEGER;
  3112. esz: CARDINAL;
  3113. bv: QVal;
  3114. BEGIN
  3115. n := SymTab.ArrayLen(t);
  3116. elem := SymTab.ArrayElem(t);
  3117. ecls := SymTab.ClassOf(elem);
  3118. App(s, "l ");
  3119. IntStr(VAL(INTEGER, n), bv);
  3120. App(s, bv);
  3121. IF ecls = SymTab.ClChar THEN
  3122. App(s, ", z "); IntStr(VAL(INTEGER, n + 1), bv); App(s, bv)
  3123. ELSIF ecls = SymTab.ClUChar THEN
  3124. App(s, ", z "); IntStr(VAL(INTEGER, (n + 1) * 4), bv); App(s, bv)
  3125. ELSE
  3126. IF (ecls = SymTab.ClReal) OR (ecls = SymTab.ClPtr)
  3127. OR (ecls = SymTab.ClProc) OR (ecls = SymTab.ClArray)
  3128. OR (ecls = SymTab.ClLong) THEN esz := 8
  3129. ELSE esz := 4
  3130. END;
  3131. IF n > 0 THEN
  3132. App(s, ", z "); IntStr(VAL(INTEGER, n * esz), bv); App(s, bv)
  3133. END
  3134. END
  3135. END ArrZeroBody;
  3136. PROCEDURE FindCtor (v: ARRAY OF CHAR): INTEGER;
  3137. VAR k: CARDINAL;
  3138. sv: QVal;
  3139. BEGIN
  3140. k := 0;
  3141. WHILE k < ctorN DO
  3142. Cpy(sv, "$"); App(sv, ctorNam[k]);
  3143. IF SymTab.Equal(sv, v) THEN RETURN VAL(INTEGER, k) END;
  3144. INC(k)
  3145. END;
  3146. RETURN -1
  3147. END FindCtor;
  3148. (* default items for a nested record field, appended to s *)
  3149. PROCEDURE RecItemDefaults (VAR s: ARRAY OF CHAR; VAR first: BOOLEAN;
  3150. prefix: ARRAY OF CHAR; t: INTEGER);
  3151. VAR i, n, cls, w: INTEGER;
  3152. fn: SymTab.Name;
  3153. ft: SymTab.TypeIndex;
  3154. sub: QVal;
  3155. BEGIN
  3156. n := VAL(INTEGER, SymTab.FieldCount(t));
  3157. i := 0;
  3158. WHILE i < n DO
  3159. SymTab.FieldName(t, i, fn);
  3160. ft := SymTab.FieldType(t, fn);
  3161. cls := SymTab.ClassOf(ft);
  3162. IF first THEN first := FALSE ELSE App(s, ", ") END;
  3163. IF cls = SymTab.ClReal THEN App(s, "d 0")
  3164. ELSIF cls = SymTab.ClChar THEN App(s, "b 0")
  3165. ELSIF cls = SymTab.ClArray THEN ArrZeroBody(s, ft)
  3166. ELSIF cls = SymTab.ClSet THEN
  3167. w := VAL(INTEGER, SymTab.SetWords(ft));
  3168. IF w = 0 THEN w := 1 END;
  3169. App(s, "w 0");
  3170. WHILE w > 1 DO App(s, ", w 0"); DEC(w) END
  3171. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  3172. Cpy(sub, prefix); App(sub, "_"); App(sub, fn);
  3173. RecItemDefaults(s, first, sub, ft)
  3174. ELSE App(s, "w 0")
  3175. END;
  3176. INC(i)
  3177. END
  3178. END RecItemDefaults;
  3179. PROCEDURE CtorBegin (t: INTEGER);
  3180. BEGIN
  3181. IF ctorTop > HIGH(ctorTyp) THEN RETURN END;
  3182. ctorTyp[ctorTop] := t;
  3183. ctorCnt[ctorTop] := 0;
  3184. ctorBuf[ctorTop][0] := CHR(0);
  3185. INC(ctorTop)
  3186. END CtorBegin;
  3187. PROCEDURE CtorElemType (t: INTEGER; cnt: CARDINAL): SymTab.TypeIndex;
  3188. (* the type of the cnt-th constructor element: array element, or the
  3189. cnt-th record field in declaration order. *)
  3190. VAR fn: SymTab.Name;
  3191. BEGIN
  3192. IF SymTab.ClassOf(t) = SymTab.ClArray THEN
  3193. RETURN SymTab.ArrayElem(t)
  3194. END;
  3195. SymTab.FieldName(t, cnt, fn);
  3196. IF fn[0] = CHR(0) THEN RETURN SymTab.InvalidType END;
  3197. RETURN SymTab.FieldType(t, fn)
  3198. END CtorElemType;
  3199. PROCEDURE StrDesc (v: ARRAY OF CHAR; elemT: INTEGER; VAR out: QVal);
  3200. (* v is a $strN descriptor; build a CHAR-array descriptor of type
  3201. elemT from its text (padded/NUL-terminated) and return "$ctoM". *)
  3202. VAR k: CARDINAL;
  3203. found: BOOLEAN;
  3204. txt, bv, sv: QVal;
  3205. buf: ARRAY [0 .. 4095] OF CHAR;
  3206. n, i, L: CARDINAL;
  3207. BEGIN
  3208. Cpy(out, v);
  3209. IF (ctorN > HIGH(ctorNam)) THEN RETURN END;
  3210. found := FALSE; k := 0;
  3211. WHILE (k < nStr) AND NOT found DO
  3212. Cpy(sv, "$"); App(sv, strNams[k]);
  3213. IF SymTab.Equal(sv, v) THEN
  3214. Cpy(txt, strTexts[k]); found := TRUE
  3215. ELSE INC(k)
  3216. END
  3217. END;
  3218. IF NOT found THEN RETURN END;
  3219. L := Len(txt);
  3220. n := SymTab.ArrayLen(elemT);
  3221. buf[0] := CHR(0);
  3222. App(buf, "l ");
  3223. IntStr(VAL(INTEGER, n), bv);
  3224. App(buf, bv);
  3225. i := 0;
  3226. WHILE i < n DO
  3227. IF (i + 1 < L - 1) AND ((i + 1) <= HIGH(txt)) THEN
  3228. IntStr(ORD(txt[i + 1]), bv)
  3229. ELSE Cpy(bv, "0")
  3230. END;
  3231. CtorItem(buf, bv, SymTab.ClChar);
  3232. INC(i)
  3233. END;
  3234. App(buf, ", b 0");
  3235. Cpy(ctorNam[ctorN], "cto"); AppNum(ctorNam[ctorN], ctorN);
  3236. Cpy(ctorTxt[ctorN], buf);
  3237. ctorUse[ctorN] := TRUE;
  3238. INC(ctorN);
  3239. Cpy(out, "$"); App(out, ctorNam[ctorN - 1])
  3240. END StrDesc;
  3241. PROCEDURE AppCtorElem (v: ARRAY OF CHAR; cls: INTEGER);
  3242. VAR cnt: CARDINAL;
  3243. BEGIN
  3244. IF ctorTop = 0 THEN RETURN END;
  3245. cnt := ctorCnt[ctorTop - 1];
  3246. IF cnt > HIGH(ctorEv[0]) THEN RETURN END;
  3247. Cpy(ctorEv[ctorTop - 1][cnt], v);
  3248. ctorEk[ctorTop - 1][cnt] := cls;
  3249. INC(ctorCnt[ctorTop - 1])
  3250. END AppCtorElem;
  3251. PROCEDURE CtorElem (v: ARRAY OF CHAR);
  3252. (* Record the next constructor element. The element type is derived
  3253. from the constructor's type and the element index. A string literal
  3254. filling a CHAR element expands into consecutive elements. *)
  3255. VAR t, elemT, cls, k, si: INTEGER;
  3256. cnt: CARDINAL;
  3257. vv, bv: QVal;
  3258. isStr: BOOLEAN;
  3259. BEGIN
  3260. IF ctorTop = 0 THEN RETURN END;
  3261. t := ctorTyp[ctorTop - 1];
  3262. elemT := CtorElemType(t, ctorCnt[ctorTop - 1]);
  3263. cls := SymTab.ClassOf(elemT);
  3264. isStr := (Len(v) > 3) AND (v[0] = "$")
  3265. AND (v[1] = "s") AND (v[2] = "t");
  3266. IF cls = SymTab.ClChar THEN
  3267. IF isStr AND FindStrPos(v, cnt) THEN
  3268. k := 1;
  3269. WHILE k < VAL(INTEGER, Len(strTexts[cnt])) - 1 DO
  3270. IntStr(ORD(strTexts[cnt][k]), bv);
  3271. AppCtorElem(bv, SymTab.ClChar);
  3272. INC(k)
  3273. END;
  3274. RETURN
  3275. END;
  3276. AppCtorElem(v, SymTab.ClChar);
  3277. RETURN
  3278. END;
  3279. Cpy(vv, v);
  3280. IF (cls = SymTab.ClArray) AND isStr
  3281. AND (SymTab.ClassOf(SymTab.ArrayElem(elemT)) = SymTab.ClChar) THEN
  3282. StrDesc(v, elemT, vv)
  3283. END;
  3284. AppCtorElem(vv, cls)
  3285. END CtorElem;
  3286. PROCEDURE NewCtorTemp (t: INTEGER; VAR q: QVal);
  3287. VAR nb: QVal;
  3288. BEGIN
  3289. NewTemp(q);
  3290. Revive;
  3291. IntStr(VAL(INTEGER, HeapSize(t)), nb);
  3292. W(" "); W(q); W(" =l alloc8 "); WL(nb)
  3293. END NewCtorTemp;
  3294. PROCEDURE InitArrHeader (addr: ARRAY OF CHAR; t: INTEGER);
  3295. (* Store an inline array's count header (and CHAR/UCHAR terminator
  3296. slot) at addr, so a following CopyArray sees matching counts. *)
  3297. VAR nb, ea: QVal;
  3298. BEGIN
  3299. IF SymTab.ClassOf(t) # SymTab.ClArray THEN RETURN END;
  3300. IntStr(VAL(INTEGER, SymTab.ArrayLen(t)), nb);
  3301. Revive; W(" storel "); W(nb); W(", "); WL(addr);
  3302. IF SymTab.IsCharArray(t) THEN
  3303. IntStr(VAL(INTEGER, 8 + SymTab.ArrayLen(t)), nb);
  3304. NewTemp(ea); Op3L("add", ea, addr, nb);
  3305. Revive; W(" storeb 0, "); WL(ea)
  3306. END;
  3307. IF SymTab.IsUCharArray(t) THEN
  3308. IntStr(VAL(INTEGER, 8 + SymTab.ArrayLen(t) * 4), nb);
  3309. NewTemp(ea); Op3L("add", ea, addr, nb);
  3310. Revive; W(" storew 0, "); WL(ea)
  3311. END
  3312. END InitArrHeader;
  3313. PROCEDURE CtorArrStatic (VAR s: ARRAY OF CHAR; lvl: CARDINAL;
  3314. t: INTEGER; cnt: CARDINAL);
  3315. VAR elem, ecls, n, k, ci: INTEGER;
  3316. v: QVal;
  3317. BEGIN
  3318. elem := SymTab.ArrayElem(t);
  3319. ecls := SymTab.ClassOf(elem);
  3320. n := VAL(INTEGER, SymTab.ArrayLen(t));
  3321. App(s, "l ");
  3322. IntStr(n, v);
  3323. App(s, v);
  3324. k := 0;
  3325. WHILE k < n DO
  3326. IF k < VAL(INTEGER, cnt) THEN
  3327. Cpy(v, ctorEv[lvl][k])
  3328. ELSE Cpy(v, "0")
  3329. END;
  3330. CtorItem(s, v, ecls);
  3331. INC(k)
  3332. END;
  3333. IF ecls = SymTab.ClChar THEN App(s, ", b 0")
  3334. ELSIF ecls = SymTab.ClUChar THEN App(s, ", w 0")
  3335. END
  3336. END CtorArrStatic;
  3337. PROCEDURE CtorRecStatic (VAR s: ARRAY OF CHAR; lvl: CARDINAL;
  3338. t: INTEGER; cnt: CARDINAL);
  3339. (* Static record body: fields in declaration order; an array/record
  3340. field given as a nested constructor is inlined (and that child
  3341. descriptor is marked consumed). *)
  3342. VAR i, n, cls, ci, w: INTEGER;
  3343. fn: SymTab.Name;
  3344. ft: SymTab.TypeIndex;
  3345. v, bv: QVal;
  3346. first: BOOLEAN;
  3347. BEGIN
  3348. first := TRUE;
  3349. IF (SymTab.ClassOf(t) = SymTab.ClClass) AND SymTab.HasVTable(t) THEN
  3350. VtRef(t, bv);
  3351. App(s, "l "); App(s, bv);
  3352. first := FALSE
  3353. END;
  3354. n := VAL(INTEGER, SymTab.FieldCount(t));
  3355. i := 0;
  3356. WHILE i < n DO
  3357. SymTab.FieldName(t, i, fn);
  3358. ft := SymTab.FieldType(t, fn);
  3359. cls := SymTab.ClassOf(ft);
  3360. v[0] := CHR(0);
  3361. IF i < VAL(INTEGER, cnt) THEN Cpy(v, ctorEv[lvl][i]) END;
  3362. IF (v[0] # CHR(0)) AND (v[0] = "$")
  3363. AND ((cls = SymTab.ClArray) OR (cls = SymTab.ClRecord)
  3364. OR (cls = SymTab.ClClass)) THEN
  3365. ci := FindCtor(v);
  3366. IF first THEN first := FALSE ELSE App(s, ", ") END;
  3367. IF ci >= 0 THEN
  3368. App(s, ctorTxt[ci]);
  3369. ctorUse[ci] := FALSE
  3370. ELSIF cls = SymTab.ClArray THEN ArrZeroBody(s, ft)
  3371. ELSE App(s, "w 0")
  3372. END
  3373. ELSIF v[0] # CHR(0) THEN
  3374. RecItem(s, first, v, cls)
  3375. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  3376. IF first THEN first := FALSE ELSE App(s, ", ") END;
  3377. Cpy(bv, ctorNam[ctorN]); App(bv, "_"); App(bv, fn);
  3378. RecItemDefaults(s, first, bv, ft)
  3379. ELSE
  3380. IF first THEN first := FALSE ELSE App(s, ", ") END;
  3381. IF cls = SymTab.ClReal THEN App(s, "d 0")
  3382. ELSIF cls = SymTab.ClChar THEN App(s, "b 0")
  3383. ELSIF cls = SymTab.ClArray THEN ArrZeroBody(s, ft)
  3384. ELSIF cls = SymTab.ClSet THEN
  3385. w := VAL(INTEGER, SymTab.SetWords(ft));
  3386. IF w = 0 THEN w := 1 END;
  3387. App(s, "w 0");
  3388. WHILE w > 1 DO App(s, ", w 0"); DEC(w) END
  3389. ELSE App(s, "w 0")
  3390. END
  3391. END;
  3392. INC(i)
  3393. END;
  3394. IF first THEN App(s, "w 0") END
  3395. END CtorRecStatic;
  3396. PROCEDURE CtorEnd (VAR q: QVal);
  3397. VAR t, cls, lvl, k, n, cnt, esz, off, ci: INTEGER;
  3398. allc: BOOLEAN;
  3399. bv, a, v: QVal;
  3400. s: ARRAY [0 .. 4095] OF CHAR;
  3401. ft, elem: SymTab.TypeIndex;
  3402. nm: SymTab.Name;
  3403. BEGIN
  3404. IF ctorTop = 0 THEN Cpy(q, "0"); RETURN END;
  3405. DEC(ctorTop);
  3406. lvl := VAL(INTEGER, ctorTop);
  3407. IF noEmit THEN Cpy(q, "0"); RETURN END;
  3408. t := ctorTyp[ctorTop];
  3409. cnt := VAL(INTEGER, ctorCnt[ctorTop]);
  3410. cls := SymTab.ClassOf(t);
  3411. IF (cls # SymTab.ClArray) AND (cls # SymTab.ClRecord)
  3412. AND (cls # SymTab.ClClass) THEN
  3413. Cpy(q, "0"); RETURN
  3414. END;
  3415. allc := TRUE; k := 0;
  3416. WHILE k < cnt DO
  3417. IF NOT IsBakeOp(ctorEv[lvl][k]) THEN allc := FALSE END;
  3418. INC(k)
  3419. END;
  3420. IF allc AND (ctorN <= HIGH(ctorNam)) THEN
  3421. s[0] := CHR(0);
  3422. IF cls = SymTab.ClArray THEN
  3423. CtorArrStatic(s, ctorTop, t, ctorCnt[ctorTop])
  3424. ELSE
  3425. CtorRecStatic(s, ctorTop, t, ctorCnt[ctorTop])
  3426. END;
  3427. Cpy(ctorNam[ctorN], "cto"); AppNum(ctorNam[ctorN], ctorN);
  3428. Cpy(ctorTxt[ctorN], s);
  3429. ctorUse[ctorN] := TRUE;
  3430. INC(ctorN);
  3431. Cpy(q, "$"); App(q, ctorNam[ctorN - 1])
  3432. ELSE
  3433. (* runtime construction: an all-zero descriptor filled in place *)
  3434. NewCtorTemp(t, q);
  3435. IF cls = SymTab.ClArray THEN
  3436. IntStr(VAL(INTEGER, SymTab.ArrayLen(t)), bv);
  3437. Revive; W(" storel "); W(bv); W(", "); WL(q);
  3438. elem := SymTab.ArrayElem(t);
  3439. esz := VAL(INTEGER, ElemSize(t));
  3440. n := VAL(INTEGER, SymTab.ArrayLen(t));
  3441. k := 0;
  3442. WHILE k < n DO
  3443. off := 8 + k * esz;
  3444. FieldAddr(q, off, a);
  3445. IF k < cnt THEN
  3446. Cpy(v, ctorEv[lvl][k]);
  3447. ElemStore(a, v, elem)
  3448. ELSE ElemStore(a, "0", elem)
  3449. END;
  3450. INC(k)
  3451. END;
  3452. IF SymTab.ClassOf(elem) = SymTab.ClChar THEN
  3453. off := 8 + n;
  3454. FieldAddr(q, off, a);
  3455. Revive; W(" storeb 0, "); WL(a)
  3456. ELSIF SymTab.ClassOf(elem) = SymTab.ClUChar THEN
  3457. off := 8 + n * 4;
  3458. FieldAddr(q, off, a);
  3459. Revive; W(" storew 0, "); WL(a)
  3460. END
  3461. ELSE
  3462. (* record / class *)
  3463. IF (cls = SymTab.ClClass) AND SymTab.HasVTable(t) THEN
  3464. VtRef(t, bv);
  3465. off := SymTab.VptrOffset(t);
  3466. FieldAddr(q, off, a);
  3467. Revive; W(" storel "); W(bv); W(", "); WL(a)
  3468. END;
  3469. n := VAL(INTEGER, SymTab.FieldCount(t));
  3470. k := 0;
  3471. WHILE k < n DO
  3472. SymTab.FieldName(t, k, nm);
  3473. ft := SymTab.FieldType(t, nm);
  3474. off := SymTab.FieldOffset(t, nm);
  3475. FieldAddr(q, off, a);
  3476. IF k < cnt THEN
  3477. Cpy(v, ctorEv[lvl][k]);
  3478. IF SymTab.ClassOf(ft) = SymTab.ClArray THEN
  3479. InitArrHeader(a, ft);
  3480. CopyArray(a, v, ft)
  3481. ELSIF (SymTab.ClassOf(ft) = SymTab.ClRecord)
  3482. OR (SymTab.ClassOf(ft) = SymTab.ClClass) THEN
  3483. CopyRecord(a, v, ft)
  3484. ELSIF SymTab.ClassOf(ft) = SymTab.ClSet THEN
  3485. CopySet(a, v, SymTab.SetWords(ft), SymTab.SetWords(ft))
  3486. ELSE ElemStore(a, v, ft)
  3487. END
  3488. ELSIF (SymTab.ClassOf(ft) = SymTab.ClReal)
  3489. OR (SymTab.ClassOf(ft) = SymTab.ClChar)
  3490. OR (SymTab.ClassOf(ft) = SymTab.ClInt)
  3491. OR (SymTab.ClassOf(ft) = SymTab.ClBool)
  3492. OR (SymTab.ClassOf(ft) = SymTab.ClEnum)
  3493. OR (SymTab.ClassOf(ft) = SymTab.ClLong)
  3494. OR (SymTab.ClassOf(ft) = SymTab.ClPtr)
  3495. OR (SymTab.ClassOf(ft) = SymTab.ClProc) THEN
  3496. ElemStore(a, "0", ft)
  3497. END;
  3498. INC(k)
  3499. END
  3500. END
  3501. END
  3502. END CtorEnd;
  3503. PROCEDURE FlushCtors;
  3504. VAR k: CARDINAL;
  3505. BEGIN
  3506. IF NOT opened THEN RETURN END;
  3507. k := 0;
  3508. WHILE k < ctorN DO
  3509. IF ctorUse[k] THEN
  3510. W("data $"); W(ctorNam[k]); W(" = { "); W(ctorTxt[k]); WL(" }")
  3511. END;
  3512. INC(k)
  3513. END
  3514. END FlushCtors;
  3515. PROCEDURE PushLoop (exit: ARRAY OF CHAR);
  3516. BEGIN
  3517. IF loopTop <= HIGH(loopSt) THEN
  3518. Cpy(loopSt[loopTop], exit); INC(loopTop)
  3519. END
  3520. END PushLoop;
  3521. PROCEDURE PopLoop;
  3522. BEGIN
  3523. IF loopTop > 0 THEN DEC(loopTop) END
  3524. END PopLoop;
  3525. PROCEDURE TopLoop (VAR exit: QVal): BOOLEAN;
  3526. BEGIN
  3527. IF loopTop = 0 THEN RETURN FALSE END;
  3528. Cpy(exit, loopSt[loopTop - 1]);
  3529. RETURN TRUE
  3530. END TopLoop;
  3531. PROCEDURE CmpL (op: INTEGER; l, r: ARRAY OF CHAR; VAR q: QVal);
  3532. VAR mn : ARRAY [0 .. 7] OF CHAR;
  3533. BEGIN
  3534. mn[0] := CHR(0);
  3535. IF op = SymTab.OpEq THEN Cpy(mn, "ceql")
  3536. ELSE Cpy(mn, "cnel")
  3537. END;
  3538. NewTemp(q);
  3539. Op3(mn, q, l, r, FALSE)
  3540. END CmpL;
  3541. (* ---------------- heap (step 3.6: extern malloc/free) ---------------- *)
  3542. (* Only the object skeleton is initialized (array counts); elements
  3543. and members stay garbage per Wirth, except nested objects which
  3544. are allocated recursively. DISPOSE is shallow (documented). *)
  3545. PROCEDURE HeapSize (t: INTEGER): CARDINAL;
  3546. VAR cls: INTEGER;
  3547. n: CARDINAL;
  3548. BEGIN
  3549. cls := SymTab.ClassOf(t);
  3550. IF cls = SymTab.ClArray THEN
  3551. n := SymTab.ArrayLen(t) * ElemSize(t);
  3552. IF SymTab.IsCharArray(t) THEN n := n + 1 END;
  3553. IF SymTab.IsUCharArray(t) THEN n := n + 4 END;
  3554. RETURN 8 + n
  3555. END;
  3556. RETURN SymTab.TypeSize(t)
  3557. END HeapSize;
  3558. PROCEDURE NewHeap (t: SymTab.TypeIndex; VAR q: QVal);
  3559. VAR nb: QVal;
  3560. BEGIN
  3561. NewTemp(q);
  3562. Revive;
  3563. IntStr(VAL(INTEGER, HeapSize(t)), nb);
  3564. W(" "); W(q);
  3565. IF useStack THEN W(" =l alloc8 "); W(nb); WL("")
  3566. ELSE W(" =l call $malloc(l "); W(nb); WL(")")
  3567. END
  3568. END NewHeap;
  3569. PROCEDURE FreeHeap (v: ARRAY OF CHAR);
  3570. BEGIN
  3571. Revive;
  3572. W(" call $free(l "); W(v); WL(")")
  3573. END FreeHeap;
  3574. PROCEDURE InitHeap (addr: ARRAY OF CHAR; t: SymTab.TypeIndex);
  3575. VAR cls, ecls: INTEGER;
  3576. n, i: CARDINAL;
  3577. fn: SymTab.Name;
  3578. ft: SymTab.TypeIndex;
  3579. nb, ea, eb, fa, fb: QVal;
  3580. BEGIN
  3581. cls := SymTab.ClassOf(t);
  3582. IF (cls = SymTab.ClClass) AND SymTab.HasVTable(t) THEN
  3583. (* install the class's vtable pointer *)
  3584. VtRef(t, eb);
  3585. IntStr(SymTab.VptrOffset(t), nb);
  3586. NewTemp(ea);
  3587. Op3L("add", ea, addr, nb);
  3588. Revive;
  3589. W(" storel "); W(eb); W(", "); WL(ea)
  3590. END;
  3591. IF cls = SymTab.ClArray THEN
  3592. IntStr(VAL(INTEGER, SymTab.ArrayLen(t)), nb);
  3593. Revive;
  3594. W(" storel "); W(nb); W(", "); WL(addr);
  3595. IF SymTab.IsCharArray(t) THEN
  3596. (* NUL terminator slot at index = count *)
  3597. IntStr(VAL(INTEGER, 8 + SymTab.ArrayLen(t)), nb);
  3598. NewTemp(ea);
  3599. Op3L("add", ea, addr, nb);
  3600. Revive;
  3601. W(" storeb 0, "); WL(ea)
  3602. END;
  3603. IF SymTab.IsUCharArray(t) THEN
  3604. (* 0-codepoint terminator at index = count (4-byte element) *)
  3605. IntStr(VAL(INTEGER, 8 + SymTab.ArrayLen(t) * 4), nb);
  3606. NewTemp(ea);
  3607. Op3L("add", ea, addr, nb);
  3608. Revive;
  3609. W(" storew 0, "); WL(ea)
  3610. END;
  3611. ecls := SymTab.ClassOf(SymTab.ArrayElem(t));
  3612. IF ecls = SymTab.ClArray THEN
  3613. n := SymTab.ArrayLen(t);
  3614. i := 0;
  3615. WHILE i < n DO
  3616. IntStr(VAL(INTEGER, 8 + i * 8), nb);
  3617. NewTemp(ea);
  3618. Op3L("add", ea, addr, nb);
  3619. NewHeap(SymTab.ArrayElem(t), eb);
  3620. InitHeap(eb, SymTab.ArrayElem(t)); (* init the sub-array *)
  3621. Revive;
  3622. W(" storel "); W(eb); W(", "); WL(ea);
  3623. INC(i)
  3624. END
  3625. END
  3626. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  3627. n := SymTab.FieldCount(t);
  3628. i := 0;
  3629. WHILE i < n DO
  3630. SymTab.FieldName(t, i, fn);
  3631. ft := SymTab.FieldType(t, fn);
  3632. ecls := SymTab.ClassOf(ft);
  3633. IF ecls = SymTab.ClArray THEN
  3634. (* inline array field: initialize its header in place *)
  3635. IntStr(SymTab.FieldOffset(t, fn), nb);
  3636. NewTemp(fa);
  3637. Op3L("add", fa, addr, nb);
  3638. InitHeap(fa, ft)
  3639. ELSIF (ecls = SymTab.ClRecord) OR (ecls = SymTab.ClClass) THEN
  3640. IntStr(SymTab.FieldOffset(t, fn), nb);
  3641. NewTemp(fa);
  3642. Op3L("add", fa, addr, nb);
  3643. InitHeap(fa, ft)
  3644. END;
  3645. INC(i)
  3646. END
  3647. END
  3648. END InitHeap;
  3649. BEGIN
  3650. opened := FALSE;
  3651. inBody := FALSE;
  3652. dead := FALSE;
  3653. nTemp := 0; nLab := 0; loopTop := 0; nR := 0; nStr := 0;
  3654. withTop := 0;
  3655. noEmit := FALSE; inFunc := FALSE; useStack := FALSE;
  3656. nLoc := 0; nPar := 0; nArg := 0; nn := 0; callDepth := 0;
  3657. recvArmed := FALSE;
  3658. funcDepth := 0; scopeTop := 0; scopeBase[0] := 0;
  3659. outSel := 0
  3660. END QbeGen.