QbeGen.mod 108 KB

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