QbeGen.mod 81 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876
  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. withTop : CARDINAL;
  29. withSt : ARRAY [0 .. 7] OF QVal;
  30. noEmit : BOOLEAN; (* TRUE while parsing nested procedures in 4.1:
  31. SymTab tracks, QbeGen suppresses output *)
  32. inFunc : BOOLEAN; (* TRUE inside an emitted function body *)
  33. useStack : BOOLEAN;(* TRUE: NewHeap allocates stack slots *)
  34. funcRes : CHAR; (* current function result class *)
  35. nLoc : CARDINAL; (* function-local name table entries *)
  36. locNames : ARRAY [0 .. 255] OF SymTab.Name;
  37. locTag : ARRAY [0 .. 255] OF INTEGER; (* 0 slot, 1 data, 2 addr *)
  38. locRep : ARRAY [0 .. 255] OF QVal; (* slot temp / mangled / addr *)
  39. locCls : ARRAY [0 .. 255] OF CHAR;
  40. locTyp : ARRAY [0 .. 255] OF INTEGER;
  41. funcDepth : CARDINAL; (* open BeginFuncs; main body = 0 *)
  42. scopeTop : CARDINAL; (* open procedure scopes *)
  43. scopeBase : ARRAY [0 .. 16] OF CARDINAL;
  44. scopeLink : ARRAY [0 .. 15] OF QVal; (* per-scope link records *)
  45. slTmp : QVal; (* current static-link param temp *)
  46. hdrComma : BOOLEAN;
  47. stkLink : ARRAY [0 .. 15] OF QVal; (* per-call static links *)
  48. stkExt : ARRAY [0 .. 15] OF BOOLEAN; (* per-call external flag *)
  49. stkInd : ARRAY [0 .. 15] OF BOOLEAN; (* per-call indirect flag *)
  50. outSel : CARDINAL; (* 0 = file, else nestBufs[outSel-1] *)
  51. nInit : CARDINAL;
  52. initNames : ARRAY [0 .. 31] OF QVal;
  53. sessBuf : ARRAY [0 .. 4194303] OF CHAR; (* whole image, buffered (4 MiB:
  54. must hold the compiler's own
  55. image text when self-hosting) *)
  56. sessUsed : CARDINAL;
  57. nestBufs : ARRAY [0 .. 15] OF ARRAY [0 .. 65535] OF CHAR;
  58. nestUsed : ARRAY [0 .. 15] OF CARDINAL;
  59. (* Short-circuit AND/OR: the RHS operand's code is buffered here
  60. (delayTop > 0) and replayed inside the branch. *)
  61. delayBuf : ARRAY [0 .. 15] OF ARRAY [0 .. 8191] OF CHAR;
  62. delayUsed : ARRAY [0 .. 15] OF CARDINAL;
  63. delayTop : CARDINAL;
  64. (* one buffer per nesting depth: each nested function stays
  65. contiguous no matter how deep the parse interleaves *)
  66. nPar : CARDINAL; (* recorded formal params for the header *)
  67. parCls : ARRAY [0 .. 63] OF CHAR;
  68. parTmp : ARRAY [0 .. 63] OF QVal;
  69. parNam : ARRAY [0 .. 63] OF SymTab.Name;
  70. parVar : ARRAY [0 .. 63] OF BOOLEAN;
  71. parTyp : ARRAY [0 .. 63] OF INTEGER;
  72. argBuf : ARRAY [0 .. 4095] OF CHAR; (* accumulated call args *)
  73. hbuf : ARRAY [0 .. 2047] OF CHAR; (* buffered function header *)
  74. nArg : CARDINAL;
  75. recvQ : QVal; (* armed class-method receiver *)
  76. recvArmed : BOOLEAN;
  77. callDepth : CARDINAL; (* nested-call stack *)
  78. stkName : ARRAY [0 .. 15] OF QVal;
  79. stkRes : ARRAY [0 .. 15] OF CHAR;
  80. stkArg : ARRAY [0 .. 15] OF ARRAY [0 .. 1023] OF CHAR;
  81. stkN : ARRAY [0 .. 15] OF CARDINAL;
  82. funcName : QVal; (* current function / call target *)
  83. curModName : SymTab.Name; (* module being compiled (global names) *)
  84. nVal : ARRAY [0 .. 255] OF QVal; (* VAR-actual note keys *)
  85. nAddr : ARRAY [0 .. 255] OF QVal; (* VAR-actual note addresses *)
  86. nn : CARDINAL;
  87. (* ---------------- small string utilities ---------------- *)
  88. PROCEDURE Len (s: ARRAY OF CHAR): CARDINAL;
  89. VAR i : CARDINAL;
  90. BEGIN
  91. i := 0;
  92. WHILE (i <= HIGH(s)) AND (s[i] # CHR(0)) DO INC(i) END;
  93. RETURN i
  94. END Len;
  95. PROCEDURE Cpy (VAR d: ARRAY OF CHAR; s: ARRAY OF CHAR);
  96. VAR i : CARDINAL;
  97. BEGIN
  98. i := 0;
  99. WHILE (i <= HIGH(d)) AND (i <= HIGH(s)) AND (s[i] # CHR(0)) DO
  100. d[i] := s[i]; INC(i)
  101. END;
  102. IF i <= HIGH(d) THEN d[i] := CHR(0) END
  103. END Cpy;
  104. PROCEDURE App (VAR d: ARRAY OF CHAR; s: ARRAY OF CHAR);
  105. VAR i, j : CARDINAL;
  106. BEGIN
  107. i := 0;
  108. WHILE (i <= HIGH(d)) AND (d[i] # CHR(0)) DO INC(i) END;
  109. j := 0;
  110. WHILE (i <= HIGH(d)) AND (j <= HIGH(s)) AND (s[j] # CHR(0)) DO
  111. d[i] := s[j]; INC(i); INC(j)
  112. END;
  113. IF i <= HIGH(d) THEN d[i] := CHR(0) END
  114. END App;
  115. PROCEDURE BufApp (VAR buf: ARRAY OF CHAR; VAR used: CARDINAL;
  116. s: ARRAY OF CHAR; eol: BOOLEAN);
  117. VAR j : CARDINAL;
  118. BEGIN
  119. j := 0;
  120. WHILE (used < HIGH(buf)) AND (j <= HIGH(s)) AND (s[j] # CHR(0)) DO
  121. buf[used] := s[j]; INC(used); INC(j)
  122. END;
  123. IF eol AND (used < HIGH(buf)) THEN
  124. buf[used] := CHR(10); INC(used)
  125. END;
  126. IF used <= HIGH(buf) THEN buf[used] := CHR(0) END
  127. END BufApp;
  128. PROCEDURE WEmit (s: ARRAY OF CHAR; eol: BOOLEAN);
  129. (* Single output sink: the session buffer, or a nesting-depth
  130. buffer for nested functions (QBE rejects definitions inside
  131. functions, so nested bodies hoist until EndModule). *)
  132. VAR b, u, j : CARDINAL;
  133. BEGIN
  134. IF NOT opened OR noEmit THEN RETURN END;
  135. IF delayTop > 0 THEN
  136. b := delayTop - 1;
  137. u := delayUsed[b]; j := 0;
  138. WHILE (u < HIGH(delayBuf[b])) AND (j <= HIGH(s))
  139. AND (s[j] # CHR(0)) DO
  140. delayBuf[b][u] := s[j]; INC(u); INC(j)
  141. END;
  142. IF eol AND (u < HIGH(delayBuf[b])) THEN
  143. delayBuf[b][u] := CHR(10); INC(u)
  144. END;
  145. IF u <= HIGH(delayBuf[b]) THEN delayBuf[b][u] := CHR(0) END;
  146. delayUsed[b] := u;
  147. RETURN
  148. END;
  149. IF outSel = 0 THEN
  150. BufApp(sessBuf, sessUsed, s, eol);
  151. RETURN
  152. END;
  153. b := outSel - 1;
  154. IF b > HIGH(nestBufs) THEN b := HIGH(nestBufs) END;
  155. u := nestUsed[b];
  156. j := 0;
  157. WHILE (u < HIGH(nestBufs[b])) AND (j <= HIGH(s)) AND (s[j] # CHR(0)) DO
  158. nestBufs[b][u] := s[j]; INC(u); INC(j)
  159. END;
  160. IF eol AND (u < HIGH(nestBufs[b])) THEN
  161. nestBufs[b][u] := CHR(10); INC(u)
  162. END;
  163. IF u <= HIGH(nestBufs[b]) THEN nestBufs[b][u] := CHR(0) END;
  164. nestUsed[b] := u
  165. END WEmit;
  166. PROCEDURE W (s: ARRAY OF CHAR);
  167. BEGIN
  168. WEmit(s, FALSE)
  169. END W;
  170. PROCEDURE WL (s: ARRAY OF CHAR);
  171. BEGIN
  172. WEmit(s, TRUE)
  173. END WL;
  174. PROCEDURE SetNoEmit (b: BOOLEAN);
  175. BEGIN
  176. noEmit := b
  177. END SetNoEmit;
  178. PROCEDURE DelayBegin;
  179. (* Start buffering output (the RHS operand of a short-circuit AND/OR). *)
  180. BEGIN
  181. IF delayTop <= HIGH(delayBuf) THEN
  182. delayUsed[delayTop] := 0;
  183. delayBuf[delayTop][0] := CHR(0);
  184. INC(delayTop)
  185. END
  186. END DelayBegin;
  187. PROCEDURE DelayEnd;
  188. (* Stop buffering (restore the previous output sink). *)
  189. BEGIN
  190. IF delayTop > 0 THEN DEC(delayTop) END
  191. END DelayEnd;
  192. PROCEDURE DelayFlush;
  193. (* Emit the buffered RHS operand's code into the current sink.
  194. DelayEnd already popped delayTop, so the just-used buffer index is
  195. delayTop itself. *)
  196. VAR b: CARDINAL;
  197. BEGIN
  198. b := delayTop;
  199. IF b <= HIGH(delayBuf) THEN WEmit(delayBuf[b], FALSE) END
  200. END DelayFlush;
  201. PROCEDURE Slot4 (VAR s: QVal);
  202. BEGIN
  203. NewTemp(s);
  204. Revive;
  205. W(" "); W(s); W(" =l alloc4 4"); WL("")
  206. END Slot4;
  207. PROCEDURE StoreW (slot: ARRAY OF CHAR; val: ARRAY OF CHAR);
  208. BEGIN
  209. Revive;
  210. W(" storew "); W(val); W(", "); WL(slot)
  211. END StoreW;
  212. PROCEDURE LoadW (slot: ARRAY OF CHAR; VAR q: QVal);
  213. BEGIN
  214. NewTemp(q);
  215. Revive;
  216. W(" "); W(q); W(" =w loadw "); WL(slot)
  217. END LoadW;
  218. PROCEDURE SetModule (name: ARRAY OF CHAR);
  219. (* Names the module whose globals are being emitted (QBE symbols
  220. become "<mod>_<name>" so separate modules never collide). *)
  221. BEGIN
  222. Cpy(curModName, name)
  223. END SetModule;
  224. PROCEDURE SymRef (name: ARRAY OF CHAR; VAR ref: QVal);
  225. (* "$<mod>_<sym>" for module-level entries (avoids cross-module
  226. collisions); "$<name>" fallback otherwise. *)
  227. VAR g : SymTab.Name;
  228. BEGIN
  229. Cpy(ref, "$");
  230. IF SymTab.GlobalRef(name, g) THEN App(ref, g)
  231. ELSE App(ref, name)
  232. END
  233. END SymRef;
  234. PROCEDURE Wc (c: CHAR);
  235. (* Writes a single class character (w/d/l). *)
  236. VAR s : ARRAY [0 .. 1] OF CHAR;
  237. BEGIN
  238. s[0] := c; s[1] := CHR(0);
  239. W(s)
  240. END Wc;
  241. PROCEDURE AppNum (VAR d: ARRAY OF CHAR; v: CARDINAL);
  242. (* Appends v in decimal to d. *)
  243. VAR buf : ARRAY [0 .. 15] OF CHAR;
  244. n, i, L : CARDINAL;
  245. BEGIN
  246. n := 0;
  247. IF v = 0 THEN buf[0] := "0"; n := 1 END;
  248. WHILE (v > 0) AND (n <= HIGH(buf)) DO
  249. buf[n] := CHR(ORD("0") + v MOD 10); v := v DIV 10; INC(n)
  250. END;
  251. i := n;
  252. WHILE i > 0 DO
  253. DEC(i);
  254. L := Len(d);
  255. IF L < HIGH(d) THEN d[L] := buf[i]; d[L + 1] := CHR(0) END
  256. END
  257. END AppNum;
  258. (* ---------------- exported helpers ---------------- *)
  259. PROCEDURE CopyOp (s: ARRAY OF CHAR; VAR d: ARRAY OF CHAR);
  260. BEGIN
  261. Cpy(d, s)
  262. END CopyOp;
  263. PROCEDURE NoteAddr (val: ARRAY OF CHAR; addr: ARRAY OF CHAR);
  264. (* Records value-temp → address mapping for VAR actuals. Ring of
  265. 256: safe because SSA temps are never reused (stale entries can
  266. never false-match, only age out on pathological expressions). *)
  267. BEGIN
  268. Cpy(nVal[nn], val);
  269. Cpy(nAddr[nn], addr);
  270. nn := nn + 1;
  271. IF nn > HIGH(nVal) THEN nn := 0 END
  272. END NoteAddr;
  273. PROCEDURE AddrOfVal (val: ARRAY OF CHAR; VAR addr: QVal): BOOLEAN;
  274. VAR i : CARDINAL;
  275. BEGIN
  276. i := 0;
  277. WHILE i <= HIGH(nVal) DO
  278. IF SymTab.Equal(nVal[i], val) THEN
  279. Cpy(addr, nAddr[i]);
  280. RETURN TRUE
  281. END;
  282. INC(i)
  283. END;
  284. RETURN FALSE
  285. END AddrOfVal;
  286. PROCEDURE AddrOf (name: ARRAY OF CHAR; VAR q: QVal);
  287. VAR idx : INTEGER;
  288. levels : CARDINAL;
  289. BEGIN
  290. IF LocFindUp(name, levels, idx) THEN
  291. IF levels = 0 THEN
  292. IF locTag[idx] = 1 THEN
  293. Cpy(q, "$");
  294. App(q, locRep[idx])
  295. ELSE
  296. Cpy(q, locRep[idx])
  297. END
  298. ELSE
  299. UpAddrOf(idx, levels, q)
  300. END;
  301. RETURN
  302. END;
  303. SymRef(name, q)
  304. END AddrOf;
  305. PROCEDURE IntStr (v: INTEGER; VAR s: QVal);
  306. VAR neg : BOOLEAN;
  307. mag : CARDINAL;
  308. buf : ARRAY [0 .. 15] OF CHAR;
  309. n, i, L : CARDINAL;
  310. t : CHAR;
  311. BEGIN
  312. neg := v < 0;
  313. IF neg THEN mag := VAL(CARDINAL, -v) ELSE mag := VAL(CARDINAL, v) END;
  314. n := 0;
  315. IF mag = 0 THEN buf[0] := "0"; n := 1 END;
  316. WHILE (mag > 0) AND (n < HIGH(buf)) DO
  317. buf[n] := CHR(ORD("0") + mag MOD 10); mag := mag DIV 10; INC(n)
  318. END;
  319. i := 0;
  320. WHILE i < n DIV 2 DO
  321. t := buf[i]; buf[i] := buf[n - 1 - i]; buf[n - 1 - i] := t; INC(i)
  322. END;
  323. s[0] := CHR(0);
  324. IF neg THEN App(s, "-") END;
  325. i := 0;
  326. WHILE i < n DO
  327. L := Len(s);
  328. IF L < HIGH(s) THEN s[L] := buf[i]; s[L + 1] := CHR(0) END;
  329. INC(i)
  330. END
  331. END IntStr;
  332. PROCEDURE HexVal (ch: CHAR): INTEGER;
  333. BEGIN
  334. IF (ch >= "0") AND (ch <= "9") THEN RETURN ORD(ch) - ORD("0") END;
  335. IF (ch >= "A") AND (ch <= "F") THEN RETURN ORD(ch) - ORD("A") + 10 END;
  336. IF (ch >= "a") AND (ch <= "f") THEN RETURN ORD(ch) - ORD("a") + 10 END;
  337. RETURN 0
  338. END HexVal;
  339. PROCEDURE NormInt (s: ARRAY OF CHAR; VAR d: QVal);
  340. (* "0xFF" -> "255", plain decimals are copied through. *)
  341. VAR n, i : CARDINAL;
  342. v : INTEGER;
  343. BEGIN
  344. n := Len(s);
  345. IF (n > 2) AND (s[0] = "0")
  346. AND ((s[1] = "x") OR (s[1] = "X")) THEN
  347. v := 0; i := 2;
  348. WHILE i < n DO v := v * 16 + HexVal(s[i]); INC(i) END;
  349. IntStr(v, d)
  350. ELSE
  351. Cpy(d, s)
  352. END
  353. END NormInt;
  354. PROCEDURE NormLit (s: ARRAY OF CHAR; VAR d: QVal; VAR isCh: BOOLEAN);
  355. (* Full V3 integer literal: octal B/b, hex H/h and C/c suffixes plus
  356. the 0x/0X prefix; C marks a character constant. The suffix letter
  357. is excluded from the digit run (HexVal would read 'B' as 11).
  358. A 0x/0X prefix takes priority over any trailing B/C/H (C-style
  359. hex digits, so 0xB is 11, never an octal suffix). *)
  360. VAR n, i, hi, base : CARDINAL;
  361. v : INTEGER;
  362. ch : CHAR;
  363. BEGIN
  364. n := Len(s);
  365. isCh := FALSE;
  366. IF n = 0 THEN Cpy(d, "0"); RETURN END;
  367. i := 0;
  368. IF (n >= 3) AND (s[0] = "0") AND ((s[1] = "x") OR (s[1] = "X")) THEN
  369. base := 16; hi := n; i := 2
  370. ELSE
  371. ch := s[n - 1];
  372. IF (ch = "B") OR (ch = "b") THEN
  373. base := 8; hi := n - 1
  374. ELSIF (ch = "H") OR (ch = "h") THEN
  375. base := 16; hi := n - 1
  376. ELSIF (ch = "C") OR (ch = "c") THEN
  377. base := 16; hi := n - 1; isCh := TRUE
  378. ELSE
  379. base := 10; hi := n
  380. END
  381. END;
  382. v := 0;
  383. WHILE i < hi DO
  384. v := v * VAL(INTEGER, base) + HexVal(s[i]); INC(i)
  385. END;
  386. IntStr(v, d)
  387. END NormLit;
  388. PROCEDURE NormReal (s: ARRAY OF CHAR; VAR d: QVal);
  389. (* "3.14" -> "d_3.14" (QBE double immediates carry a d_ prefix). *)
  390. BEGIN
  391. Cpy(d, "d_");
  392. App(d, s)
  393. END NormReal;
  394. PROCEDURE CharVal (s: ARRAY OF CHAR): INTEGER;
  395. BEGIN
  396. IF Len(s) >= 2 THEN RETURN ORD(s[1]) END;
  397. RETURN 0
  398. END CharVal;
  399. PROCEDURE NegFold (a: ARRAY OF CHAR; VAR q: QVal);
  400. (* Safe for a and q being the same variable. *)
  401. VAR i, j, L : CARDINAL;
  402. tmp : QVal;
  403. BEGIN
  404. Cpy(tmp, a);
  405. IF Len(tmp) = 0 THEN Cpy(q, "0"); RETURN END;
  406. IF tmp[0] = "-" THEN
  407. i := 1; j := 0;
  408. WHILE (tmp[i] # CHR(0)) AND (j < HIGH(q)) DO
  409. q[j] := tmp[i]; INC(i); INC(j)
  410. END;
  411. IF j <= HIGH(q) THEN q[j] := CHR(0) END
  412. ELSIF (tmp[0] = "d") AND (Len(tmp) > 1) AND (tmp[1] = "_") THEN
  413. Cpy(q, "d_-");
  414. i := 2;
  415. WHILE tmp[i] # CHR(0) DO
  416. L := Len(q);
  417. IF L < HIGH(q) THEN q[L] := tmp[i]; q[L + 1] := CHR(0) END;
  418. INC(i)
  419. END
  420. ELSE
  421. Cpy(q, "-");
  422. App(q, tmp)
  423. END
  424. END NegFold;
  425. PROCEDURE IsImm (s: ARRAY OF CHAR): BOOLEAN;
  426. BEGIN
  427. IF Len(s) = 0 THEN RETURN FALSE END;
  428. RETURN ((s[0] >= "0") AND (s[0] <= "9")) OR (s[0] = "-")
  429. OR (s[0] = "d")
  430. END IsImm;
  431. PROCEDURE ParseInt (s: ARRAY OF CHAR; VAR v: INTEGER): BOOLEAN;
  432. VAR i, nd, d: CARDINAL;
  433. neg: BOOLEAN;
  434. BEGIN
  435. v := 0; i := 0; neg := FALSE; nd := 0;
  436. IF (i <= HIGH(s)) AND (s[i] = "-") THEN neg := TRUE; INC(i) END;
  437. IF (i > HIGH(s)) OR (s[i] = CHR(0)) THEN RETURN FALSE END;
  438. WHILE (i <= HIGH(s)) AND (s[i] # CHR(0)) DO
  439. d := ORD(s[i]);
  440. IF (d < ORD("0")) OR (d > ORD("9")) THEN RETURN FALSE END;
  441. v := v * 10 + VAL(INTEGER, d - ORD("0"));
  442. INC(i); INC(nd)
  443. END;
  444. IF nd = 0 THEN RETURN FALSE END;
  445. IF neg THEN v := -v END;
  446. RETURN TRUE
  447. END ParseInt;
  448. PROCEDURE Fold2 (kind: INTEGER; a, b: ARRAY OF CHAR; VAR r: QVal): BOOLEAN;
  449. VAR x, y, z: INTEGER;
  450. BEGIN
  451. IF NOT ParseInt(a, x) THEN RETURN FALSE END;
  452. IF NOT ParseInt(b, y) THEN RETURN FALSE END;
  453. IF kind = 0 THEN z := x + y
  454. ELSIF kind = 1 THEN z := x - y
  455. ELSIF kind = 2 THEN z := x * y
  456. ELSIF kind = 3 THEN
  457. IF y = 0 THEN RETURN FALSE END;
  458. z := x DIV y
  459. ELSE
  460. IF y = 0 THEN RETURN FALSE END;
  461. z := x MOD y
  462. END;
  463. IntStr(z, r);
  464. RETURN TRUE
  465. END Fold2;
  466. (* ---------------- module / data section ---------------- *)
  467. PROCEDURE OpenModule (name: ARRAY OF CHAR);
  468. VAR i : CARDINAL;
  469. BEGIN
  470. opened := TRUE;
  471. inBody := FALSE;
  472. dead := FALSE;
  473. nTemp := 0; nLab := 0; loopTop := 0; nR := 0; nStr := 0;
  474. withTop := 0;
  475. noEmit := FALSE; inFunc := FALSE; useStack := FALSE;
  476. nLoc := 0; nPar := 0; nArg := 0; nn := 0; callDepth := 0;
  477. recvArmed := FALSE;
  478. funcDepth := 0; scopeTop := 0; scopeBase[0] := 0;
  479. nInit := 0;
  480. outSel := 0;
  481. sessUsed := 0; sessBuf[0] := CHR(0);
  482. delayTop := 0;
  483. ustrN := 0; ustrUsed := 0;
  484. i := 0;
  485. WHILE i <= HIGH(nestBufs) DO
  486. nestBufs[i][0] := CHR(0); nestUsed[i] := 0; INC(i)
  487. END;
  488. WL("# QBE IR generated by the V3 step-1 backend");
  489. WL("")
  490. END OpenModule;
  491. PROCEDURE DataLine (name: ARRAY OF CHAR; isReal: BOOLEAN;
  492. init: ARRAY OF CHAR);
  493. BEGIN
  494. IF NOT opened THEN RETURN END;
  495. W("data $"); W(name);
  496. IF isReal THEN W(" = { d ") ELSE W(" = { w ") END;
  497. W(init);
  498. WL(" }")
  499. END DataLine;
  500. PROCEDURE DataLineL (name: ARRAY OF CHAR; init: ARRAY OF CHAR);
  501. BEGIN
  502. IF NOT opened THEN RETURN END;
  503. W("data $"); W(name);
  504. W(" = { l ");
  505. W(init);
  506. WL(" }")
  507. END DataLineL;
  508. PROCEDURE DeclLocal (name: ARRAY OF CHAR; t: INTEGER);
  509. (* Function-local variable: stack slot + static init, recorded. *)
  510. VAR slot : QVal;
  511. BEGIN
  512. AllocLocal(t, slot);
  513. LocAdd(name, 0, slot, ResClass(t), t)
  514. END DeclLocal;
  515. PROCEDURE DeclVar (name: ARRAY OF CHAR; t: INTEGER);
  516. (* Module-level variable: emitted as data "$<mod>_<name>". *)
  517. VAR g : QVal;
  518. BEGIN
  519. IF inFunc THEN DeclLocal(name, t); RETURN END;
  520. Cpy(g, curModName); App(g, "_"); App(g, name);
  521. IF SymTab.ClassOf(t) = SymTab.ClReal THEN DataLine(g, TRUE, "0")
  522. ELSIF SymTab.ClassOf(t) = SymTab.ClArray THEN DeclArr(g, t)
  523. ELSIF SymTab.ClassOf(t) = SymTab.ClSet THEN DeclSet(g, t)
  524. ELSIF (SymTab.ClassOf(t) = SymTab.ClRecord)
  525. OR (SymTab.ClassOf(t) = SymTab.ClClass) THEN
  526. DeclRec(g, t)
  527. ELSIF (SymTab.ClassOf(t) = SymTab.ClPtr)
  528. OR (SymTab.ClassOf(t) = SymTab.ClLong)
  529. OR (SymTab.ClassOf(t) = SymTab.ClProc) THEN DataLineL(g, "0")
  530. ELSE DataLine(g, FALSE, "0")
  531. END
  532. END DeclVar;
  533. PROCEDURE IsZeroReal (val: ARRAY OF CHAR): BOOLEAN;
  534. (* TRUE when the "d_..." literal denotes zero (checked textually). *)
  535. VAR i : CARDINAL;
  536. seen : BOOLEAN;
  537. ch : CHAR;
  538. BEGIN
  539. i := 0;
  540. IF (Len(val) > 1) AND (val[0] = "d") AND (val[1] = "_") THEN i := 2 END;
  541. IF (Len(val) > i) AND (val[i] = "-") THEN INC(i) END;
  542. seen := FALSE;
  543. WHILE val[i] # CHR(0) DO
  544. ch := val[i];
  545. IF (ch >= "0") AND (ch <= "9") THEN
  546. seen := TRUE;
  547. IF ch # "0" THEN RETURN FALSE END
  548. ELSIF (ch # ".") AND (ch # "E") AND (ch # "e")
  549. AND (ch # "+") AND (ch # "-") THEN
  550. RETURN FALSE
  551. END;
  552. INC(i)
  553. END;
  554. RETURN seen
  555. END IsZeroReal;
  556. PROCEDURE DeclConst (name: ARRAY OF CHAR; val: ARRAY OF CHAR; t: INTEGER);
  557. VAR cls : INTEGER;
  558. mang : QVal;
  559. BEGIN
  560. cls := SymTab.ClassOf(t);
  561. IF inFunc THEN
  562. Cpy(mang, funcName); App(mang, "_"); App(mang, name);
  563. LocAdd(name, 1, mang, ResClass(t), t)
  564. ELSE
  565. Cpy(mang, curModName); App(mang, "_"); App(mang, name)
  566. END;
  567. IF (cls = SymTab.ClStr) OR NOT IsImm(val) THEN
  568. DataLine(mang, FALSE, "0"); RETURN
  569. END;
  570. IF cls = SymTab.ClReal THEN
  571. (* This qbe accepts only integer/zero literals in "data":
  572. non-zero REALs flush at BeginBody. *)
  573. DataLine(mang, TRUE, "0");
  574. IF NOT IsZeroReal(val) AND (nR <= HIGH(rNames)) THEN
  575. Cpy(rNames[nR], mang); Cpy(rVals[nR], val); INC(nR)
  576. END
  577. ELSE DataLine(mang, FALSE, val)
  578. END
  579. END DeclConst;
  580. (* ---------------- function body ---------------- *)
  581. PROCEDURE BeginInit (mod: ARRAY OF CHAR);
  582. (* Module BEGIN body as `<mod>_init`; main calls it at startup. *)
  583. VAR sym : QVal;
  584. BEGIN
  585. IF NOT opened THEN RETURN END;
  586. Cpy(sym, mod);
  587. App(sym, "_init");
  588. WL("");
  589. W("export function w $"); W(sym); WL("() {");
  590. WL("@start");
  591. IF nInit <= HIGH(initNames) THEN
  592. Cpy(initNames[nInit], sym); INC(nInit)
  593. END
  594. END BeginInit;
  595. PROCEDURE EndInit;
  596. BEGIN
  597. Revive;
  598. WL(" ret 0");
  599. WL("}")
  600. END EndInit;
  601. PROCEDURE BeginBody;
  602. VAR i : CARDINAL;
  603. t : QVal;
  604. BEGIN
  605. IF NOT opened OR inBody THEN RETURN END;
  606. WL("");
  607. WL("export function w $main() {");
  608. WL("@start");
  609. inBody := TRUE;
  610. i := 0;
  611. WHILE i < nR DO
  612. NewTemp(t);
  613. W(" "); W(t); W(" =d copy "); WL(rVals[i]);
  614. StoreVar(rNames[i], t, TRUE);
  615. INC(i)
  616. END;
  617. i := 0;
  618. WHILE i < nInit DO
  619. W(" call $"); W(initNames[i]); WL("()");
  620. INC(i)
  621. END
  622. END BeginBody;
  623. PROCEDURE CloseModule;
  624. BEGIN
  625. IF NOT opened THEN RETURN END;
  626. WL("# no code: unit lowering waits for step 4");
  627. FileIO.Close(out);
  628. opened := FALSE
  629. END CloseModule;
  630. PROCEDURE EndModule (name: ARRAY OF CHAR);
  631. VAR i : CARDINAL;
  632. fname : ARRAY [0 .. 127] OF CHAR;
  633. ref : QVal;
  634. BEGIN
  635. IF NOT opened THEN RETURN END;
  636. IF NOT inBody THEN BeginBody END;
  637. IF SymTab.Lookup("ExitCode")
  638. AND (SymTab.SymKind("ExitCode") = SymTab.KindVar)
  639. AND SymTab.IsIntFamily(SymTab.SymType("ExitCode")) THEN
  640. Cpy(ref, "$"); App(ref, name); App(ref, "_ExitCode");
  641. W(" %ec =w loadw "); WL(ref);
  642. WL(" ret %ec")
  643. ELSE
  644. WL(" ret 0")
  645. END;
  646. WL("}");
  647. (* hoisted nested functions, then deferred data *)
  648. i := 0;
  649. WHILE i <= HIGH(nestBufs) DO
  650. IF nestUsed[i] > 0 THEN
  651. BufApp(sessBuf, sessUsed, nestBufs[i], FALSE)
  652. END;
  653. INC(i)
  654. END;
  655. FlushStrings;
  656. FlushUStrings;
  657. EmitVTables;
  658. (* the image is named after the program module *)
  659. fname[0] := CHR(0);
  660. App(fname, "gen_ssa/");
  661. App(fname, name);
  662. App(fname, ".ssa");
  663. FileIO.Open(out, fname, TRUE);
  664. IF FileIO.Okay THEN
  665. (* Flush exactly sessUsed bytes. FileIO.WriteString scans for a
  666. NUL and misbehaves for multi-megabyte buffers, so write the
  667. image byte by byte up to sessUsed. *)
  668. i := 0;
  669. WHILE i < sessUsed DO
  670. FileIO.Write(out, sessBuf[i]); INC(i)
  671. END
  672. END;
  673. FileIO.Close(out);
  674. opened := FALSE
  675. END EndModule;
  676. (* ---------------- functions and calls (step 4.1) ---------------- *)
  677. (* Module-level procedures only (nested → NoEmit until 4.2).
  678. Value params arrive as SSA values and are copied to slots;
  679. composite value params arrive as addresses and are copied;
  680. VAR params stay addresses. Locals live in stack slots. *)
  681. PROCEDURE LocFind (name: ARRAY OF CHAR): INTEGER;
  682. VAR i : CARDINAL;
  683. BEGIN
  684. i := 0;
  685. WHILE i < nLoc DO
  686. IF SymTab.Equal(locNames[i], name) THEN RETURN VAL(INTEGER, i) END;
  687. INC(i)
  688. END;
  689. RETURN -1
  690. END LocFind;
  691. PROCEDURE ScopeBegin;
  692. (* Pushes a procedure scope (always balanced, even under NoEmit). *)
  693. BEGIN
  694. IF scopeTop > HIGH(scopeLink) THEN RETURN END;
  695. scopeBase[scopeTop] := nLoc;
  696. INC(scopeTop);
  697. scopeBase[scopeTop] := nLoc
  698. END ScopeBegin;
  699. PROCEDURE ScopeEnd;
  700. BEGIN
  701. IF scopeTop > 0 THEN
  702. DEC(scopeTop);
  703. nLoc := scopeBase[scopeTop]
  704. END
  705. END ScopeEnd;
  706. PROCEDURE LocFindUp (name: ARRAY OF CHAR; VAR levels: CARDINAL;
  707. VAR flat: INTEGER): BOOLEAN;
  708. (* Innermost procedure scope holding name; levels = scopes crossed,
  709. flat = table index. FALSE when purely global. *)
  710. VAR s, top, lo, hi, j : CARDINAL;
  711. BEGIN
  712. levels := 0; flat := -1;
  713. IF scopeTop = 0 THEN RETURN FALSE END;
  714. top := scopeTop - 1;
  715. s := top;
  716. LOOP
  717. lo := scopeBase[s];
  718. IF s = top THEN hi := nLoc ELSE hi := scopeBase[s + 1] END;
  719. j := lo;
  720. WHILE j < hi DO
  721. IF SymTab.Equal(locNames[j], name) THEN
  722. levels := top - s;
  723. flat := VAL(INTEGER, j);
  724. RETURN TRUE
  725. END;
  726. INC(j)
  727. END;
  728. IF s = 0 THEN RETURN FALSE END;
  729. DEC(s)
  730. END
  731. END LocFindUp;
  732. PROCEDURE LocAdd (name: ARRAY OF CHAR; tag: INTEGER; rep: ARRAY OF CHAR;
  733. cls: CHAR; typ: INTEGER);
  734. VAR idx : CARDINAL;
  735. off : QVal;
  736. lk : QVal;
  737. BEGIN
  738. IF noEmit THEN RETURN END;
  739. IF (scopeTop = 0) OR (nLoc > HIGH(locNames)) THEN RETURN END;
  740. idx := nLoc - scopeBase[scopeTop - 1];
  741. IF idx > 63 THEN RETURN END;
  742. Cpy(locNames[nLoc], name);
  743. locTag[nLoc] := tag;
  744. Cpy(locRep[nLoc], rep);
  745. locCls[nLoc] := cls;
  746. locTyp[nLoc] := typ;
  747. INC(nLoc);
  748. IF tag = 1 THEN RETURN END;
  749. Cpy(lk, scopeLink[scopeTop - 1]);
  750. IntStr(VAL(INTEGER, 8 * (idx + 1)), off);
  751. NewTemp(lk);
  752. Op3L("add", lk, scopeLink[scopeTop - 1], off);
  753. Revive;
  754. W(" storel "); W(rep); W(", "); WL(lk)
  755. END LocAdd;
  756. PROCEDURE LocFull (): BOOLEAN;
  757. (* TRUE past 64 locals in the current scope (→ 233). *)
  758. BEGIN
  759. IF scopeTop = 0 THEN RETURN FALSE END;
  760. RETURN nLoc - scopeBase[scopeTop - 1] > 63
  761. END LocFull;
  762. PROCEDURE UpAddr (levels: CARDINAL; rel: CARDINAL; VAR q: QVal);
  763. (* Address of an up-level local: walks the static chain, then
  764. loads the link cell (slot address or actual address). *)
  765. VAR cur, t, off : QVal;
  766. L : CARDINAL;
  767. BEGIN
  768. Cpy(cur, scopeLink[scopeTop - 1]);
  769. L := levels;
  770. WHILE L > 0 DO
  771. NewTemp(t);
  772. Revive;
  773. W(" "); W(t); W(" =l loadl "); WL(cur);
  774. Cpy(cur, t);
  775. DEC(L)
  776. END;
  777. IntStr(VAL(INTEGER, 8 * (rel + 1)), off);
  778. NewTemp(t);
  779. Op3L("add", t, cur, off);
  780. NewTemp(q);
  781. Revive;
  782. W(" "); W(q); W(" =l loadl "); WL(t)
  783. END UpAddr;
  784. PROCEDURE ResClass (t: INTEGER): CHAR;
  785. VAR cls : INTEGER;
  786. BEGIN
  787. cls := SymTab.ClassOf(t);
  788. IF cls = SymTab.ClReal THEN RETURN "d" END;
  789. IF (cls = SymTab.ClArray) OR (cls = SymTab.ClSet)
  790. OR (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass)
  791. OR (cls = SymTab.ClPtr) OR (cls = SymTab.ClLong)
  792. OR (cls = SymTab.ClProc) THEN
  793. RETURN "l"
  794. END;
  795. RETURN "w"
  796. END ResClass;
  797. PROCEDURE Mangled (pname: ARRAY OF CHAR; uid: CARDINAL; VAR q: QVal);
  798. (* External procedures bind to their C link name; everything else
  799. mangles to <name>_<uid> (deterministic, collision-free). *)
  800. VAR link, base : SymTab.Name;
  801. BEGIN
  802. IF SymTab.ProcLink(pname, link) THEN
  803. Cpy(q, link);
  804. RETURN
  805. END;
  806. IF NOT SymTab.SymBase(pname, base) THEN Cpy(base, pname) END;
  807. Cpy(q, base);
  808. App(q, "_");
  809. AppNum(q, uid)
  810. END Mangled;
  811. PROCEDURE AllocLocal (t: INTEGER; VAR slot: QVal);
  812. (* Stack slot sized for t's inline footprint; arrays/records get
  813. full objects + static init. Scalar/pointer/set slots are
  814. zero-initialized: QBE rejects reads of never-stored slots, and
  815. the frontend emits a dead load for assignment targets (Test2
  816. heritage, harmless for zero-initialized globals). Locals match
  817. the globals' zero-init convention. *)
  818. VAR cls : INTEGER;
  819. nb : QVal;
  820. BEGIN
  821. cls := SymTab.ClassOf(t);
  822. NewTemp(slot);
  823. Revive;
  824. W(" "); W(slot);
  825. IF (cls = SymTab.ClArray) THEN
  826. IntStr(VAL(INTEGER, HeapSize(t)), nb);
  827. W(" =l alloc8 "); WL(nb);
  828. InitStack(slot, t)
  829. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  830. IntStr(VAL(INTEGER, SymTab.TypeSize(t)), nb);
  831. W(" =l alloc8 "); WL(nb);
  832. InitStack(slot, t)
  833. ELSIF cls = SymTab.ClSet THEN
  834. IntStr(VAL(INTEGER, SymTab.SetWords(t) * 4), nb);
  835. W(" =l alloc8 "); WL(nb);
  836. SetZero(slot, SymTab.SetWords(t))
  837. ELSIF cls = SymTab.ClReal THEN
  838. W(" =l alloc8 8"); WL("");
  839. W(" stored d_0.0, "); WL(slot)
  840. ELSIF (cls = SymTab.ClPtr) OR (cls = SymTab.ClLong)
  841. OR (cls = SymTab.ClProc) THEN
  842. W(" =l alloc8 8"); WL("");
  843. W(" storel 0, "); WL(slot)
  844. ELSE
  845. W(" =l alloc4 4"); WL("");
  846. W(" storew 0, "); WL(slot)
  847. END
  848. END AllocLocal;
  849. PROCEDURE BeginFunc (mangled: ARRAY OF CHAR);
  850. (* Opens function context and scope; the header (with a leading
  851. static-link param) buffers until EndFuncHeader. FORWARD/DefUnit
  852. paths discard via AbortFunc. *)
  853. BEGIN
  854. ScopeBegin;
  855. INC(funcDepth);
  856. inFunc := TRUE;
  857. nPar := 0;
  858. funcRes := "w";
  859. Cpy(funcName, mangled);
  860. hbuf[0] := CHR(0);
  861. NewTemp(slTmp);
  862. HApp("l ");
  863. HApp(slTmp);
  864. hdrComma := TRUE;
  865. IF funcDepth > 1 THEN
  866. outSel := funcDepth - 1;
  867. IF outSel > HIGH(nestBufs) + 1 THEN
  868. outSel := HIGH(nestBufs) + 1
  869. END
  870. ELSE
  871. outSel := 0
  872. END
  873. END BeginFunc;
  874. PROCEDURE SetFuncRes (t: INTEGER);
  875. BEGIN
  876. funcRes := ResClass(t)
  877. END SetFuncRes;
  878. PROCEDURE HApp (s: ARRAY OF CHAR);
  879. (* Appends to the header buffer (bounded: FormalParams cap it). *)
  880. VAR i, j : CARDINAL;
  881. BEGIN
  882. i := 0;
  883. WHILE (i <= HIGH(hbuf)) AND (hbuf[i] # CHR(0)) DO INC(i) END;
  884. j := 0;
  885. WHILE (i < HIGH(hbuf)) AND (j <= HIGH(s)) AND (s[j] # CHR(0)) DO
  886. hbuf[i] := s[j]; INC(i); INC(j)
  887. END;
  888. IF i <= HIGH(hbuf) THEN hbuf[i] := CHR(0) END
  889. END HApp;
  890. PROCEDURE FuncParam (name: ARRAY OF CHAR; isVar: BOOLEAN;
  891. t: INTEGER): BOOLEAN;
  892. VAR c : CHAR;
  893. tmp : QVal;
  894. buf : ARRAY [0 .. 71] OF CHAR;
  895. BEGIN
  896. IF noEmit THEN RETURN TRUE END;
  897. IF nPar > HIGH(parCls) THEN RETURN FALSE END;
  898. IF isVar THEN c := "l" ELSE c := ResClass(t) END;
  899. parCls[nPar] := c;
  900. NewTemp(tmp);
  901. Cpy(parTmp[nPar], tmp);
  902. Cpy(parNam[nPar], name);
  903. parVar[nPar] := isVar;
  904. parTyp[nPar] := t;
  905. IF hdrComma THEN HApp(", ") END;
  906. hdrComma := TRUE;
  907. buf[0] := c; buf[1] := " "; buf[2] := CHR(0);
  908. App(buf, tmp);
  909. HApp(buf);
  910. INC(nPar);
  911. RETURN TRUE
  912. END FuncParam;
  913. PROCEDURE EndFuncHeader;
  914. (* Flushes the buffered header + body label, allocates the link
  915. record ([0] = parent link), then emits entry copies. *)
  916. VAR i : CARDINAL;
  917. slot, nb, lk : QVal;
  918. cls : INTEGER;
  919. BEGIN
  920. W("export function "); Wc(funcRes); W(" $"); W(funcName); W("(");
  921. W(hbuf);
  922. WL(") {");
  923. WL("@start");
  924. inBody := TRUE;
  925. NewTemp(lk);
  926. Cpy(scopeLink[scopeTop - 1], lk);
  927. Revive;
  928. W(" "); W(lk); W(" =l alloc8 520"); WL("");
  929. W(" storel "); W(slTmp); W(", "); WL(lk);
  930. i := 0;
  931. WHILE i < nPar DO
  932. cls := SymTab.ClassOf(parTyp[i]);
  933. IF parVar[i] THEN
  934. LocAdd(parNam[i], 2, parTmp[i], parCls[i], parTyp[i])
  935. ELSE
  936. AllocLocal(parTyp[i], slot);
  937. LocAdd(parNam[i], 0, slot, parCls[i], parTyp[i]);
  938. IF cls = SymTab.ClArray THEN
  939. CopyArray(slot, parTmp[i], parTyp[i])
  940. ELSIF cls = SymTab.ClSet THEN
  941. CopySet(slot, parTmp[i],
  942. SymTab.SetWords(parTyp[i]), SymTab.SetWords(parTyp[i]))
  943. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  944. CopyRecord(slot, parTmp[i], parTyp[i])
  945. ELSIF cls = SymTab.ClReal THEN
  946. Revive;
  947. W(" stored "); W(parTmp[i]); W(", "); WL(slot)
  948. ELSIF cls = SymTab.ClPtr THEN
  949. Revive;
  950. W(" storel "); W(parTmp[i]); W(", "); WL(slot)
  951. ELSE
  952. Revive;
  953. W(" storew "); W(parTmp[i]); W(", "); WL(slot)
  954. END
  955. END;
  956. INC(i)
  957. END
  958. END EndFuncHeader;
  959. PROCEDURE AbortFunc;
  960. (* Discards function context without emitting (DefUnit headings,
  961. FORWARD, methods): pops the scope, restores the depth. *)
  962. BEGIN
  963. ScopeEnd;
  964. IF funcDepth > 0 THEN DEC(funcDepth) END;
  965. inFunc := funcDepth > 0;
  966. inBody := FALSE;
  967. nPar := 0;
  968. IF funcDepth > 1 THEN outSel := funcDepth - 1
  969. ELSE outSel := 0
  970. END
  971. END AbortFunc;
  972. PROCEDURE EndFunc (resT: INTEGER);
  973. VAR rc : CHAR;
  974. BEGIN
  975. rc := ResClass(resT);
  976. Revive;
  977. IF rc = "d" THEN WL(" ret d_0.0")
  978. ELSIF rc = "l" THEN WL(" ret 0")
  979. ELSE WL(" ret 0")
  980. END;
  981. WL("}");
  982. ScopeEnd;
  983. IF funcDepth > 0 THEN DEC(funcDepth) END;
  984. inFunc := funcDepth > 0;
  985. inBody := FALSE;
  986. IF funcDepth > 1 THEN outSel := funcDepth - 1
  987. ELSE outSel := 0
  988. END
  989. END EndFunc;
  990. PROCEDURE EmitRet (q: ARRAY OF CHAR; hasVal: BOOLEAN);
  991. BEGIN
  992. Revive;
  993. IF hasVal THEN W(" ret "); WL(q)
  994. ELSE WL(" ret 0")
  995. END;
  996. dead := TRUE
  997. END EmitRet;
  998. PROCEDURE ProcAddr (mangled: ARRAY OF CHAR; VAR q: QVal);
  999. BEGIN
  1000. Cpy(q, "$");
  1001. App(q, mangled)
  1002. END ProcAddr;
  1003. PROCEDURE CallBeginInd (callee: ARRAY OF CHAR; resT: INTEGER;
  1004. isExt: BOOLEAN);
  1005. (* Indirect call through the code pointer in `callee`. *)
  1006. BEGIN
  1007. IF callDepth > HIGH(stkName) THEN RETURN END;
  1008. Cpy(stkName[callDepth], callee);
  1009. stkRes[callDepth] := ResClass(resT);
  1010. stkExt[callDepth] := isExt;
  1011. stkInd[callDepth] := TRUE;
  1012. Cpy(stkLink[callDepth], "0");
  1013. stkArg[callDepth][0] := CHR(0);
  1014. stkN[callDepth] := 0;
  1015. INC(callDepth);
  1016. IF recvArmed THEN
  1017. IF NOT CallArg(recvQ, "l") THEN
  1018. END;
  1019. recvArmed := FALSE
  1020. END
  1021. END CallBeginInd;
  1022. PROCEDURE VirtCallBegin (obj: ARRAY OF CHAR; slot: INTEGER; resT: INTEGER);
  1023. (* Virtual dispatch: load obj's vtable pointer, fetch slot `slot`,
  1024. and begin an indirect call through it. The receiver (obj) must be
  1025. armed (ArmRecv). *)
  1026. VAR vt, off, ea, fp: QVal;
  1027. BEGIN
  1028. NewTemp(vt);
  1029. W(" "); W(vt); W(" =l loadl "); WL(obj);
  1030. IntStr(slot * 8, off);
  1031. NewTemp(ea);
  1032. Op3L("add", ea, vt, off);
  1033. NewTemp(fp);
  1034. W(" "); W(fp); W(" =l loadl "); WL(ea);
  1035. CallBeginInd(fp, resT, FALSE)
  1036. END VirtCallBegin;
  1037. PROCEDURE ArmRecv (q: ARRAY OF CHAR);
  1038. (* Arms the receiver for the next class-method call: CallBegin passes
  1039. it as the hidden first argument. *)
  1040. BEGIN
  1041. Cpy(recvQ, q);
  1042. recvArmed := TRUE
  1043. END ArmRecv;
  1044. PROCEDURE CallBegin (mangled: ARRAY OF CHAR; resT: INTEGER;
  1045. fdep: CARDINAL; isExt: BOOLEAN);
  1046. (* Pushes a call level; the static link is resolved now (caller
  1047. context cannot change mid-call): module callers pass 0, others
  1048. walk the chain (inFuncDepth - fdep) from their link record.
  1049. External callees take no static link. *)
  1050. VAR walks : INTEGER;
  1051. cur, t : QVal;
  1052. BEGIN
  1053. IF callDepth > HIGH(stkName) THEN RETURN END;
  1054. Cpy(stkName[callDepth], mangled);
  1055. stkRes[callDepth] := ResClass(resT);
  1056. stkExt[callDepth] := isExt;
  1057. stkInd[callDepth] := FALSE;
  1058. stkArg[callDepth][0] := CHR(0);
  1059. stkN[callDepth] := 0;
  1060. IF isExt THEN
  1061. Cpy(stkLink[callDepth], "0")
  1062. ELSIF funcDepth = 0 THEN
  1063. Cpy(stkLink[callDepth], "0")
  1064. ELSE
  1065. walks := VAL(INTEGER, funcDepth) - VAL(INTEGER, fdep);
  1066. Cpy(cur, scopeLink[scopeTop - 1]);
  1067. WHILE walks > 0 DO
  1068. NewTemp(t);
  1069. Revive;
  1070. W(" "); W(t); W(" =l loadl "); WL(cur);
  1071. Cpy(cur, t);
  1072. DEC(walks)
  1073. END;
  1074. Cpy(stkLink[callDepth], cur)
  1075. END;
  1076. INC(callDepth);
  1077. IF recvArmed THEN
  1078. (* class method: the receiver is the hidden first argument *)
  1079. IF NOT CallArg(recvQ, "l") THEN
  1080. END;
  1081. recvArmed := FALSE
  1082. END
  1083. END CallBegin;
  1084. PROCEDURE CallArg (q: ARRAY OF CHAR; cls: CHAR): BOOLEAN;
  1085. (* Appends "c operand, " to the current level. FALSE when full. *)
  1086. VAR need, have, i, j, d : CARDINAL;
  1087. BEGIN
  1088. d := callDepth - 1;
  1089. IF callDepth = 0 THEN RETURN FALSE END;
  1090. IF d > HIGH(stkArg) THEN d := HIGH(stkArg) END;
  1091. need := Len(q) + 5;
  1092. have := Len(stkArg[d]);
  1093. IF (stkN[d] >= 64) OR (have + need > HIGH(stkArg[d])) THEN
  1094. RETURN FALSE
  1095. END;
  1096. i := have; j := 0;
  1097. stkArg[d][i] := cls; INC(i);
  1098. stkArg[d][i] := " "; INC(i);
  1099. WHILE q[j] # CHR(0) DO
  1100. stkArg[d][i] := q[j]; INC(i); INC(j)
  1101. END;
  1102. stkArg[d][i] := ","; INC(i);
  1103. stkArg[d][i] := " "; INC(i);
  1104. stkArg[d][i] := CHR(0);
  1105. INC(stkN[d]);
  1106. RETURN TRUE
  1107. END CallArg;
  1108. PROCEDURE CallEnd (wantRes: BOOLEAN; VAR q: QVal);
  1109. VAR i, d : CARDINAL;
  1110. rc : CHAR;
  1111. body : ARRAY [0 .. 1023] OF CHAR;
  1112. BEGIN
  1113. IF callDepth = 0 THEN Cpy(q, "0"); RETURN END;
  1114. DEC(callDepth);
  1115. d := callDepth;
  1116. IF d > HIGH(stkArg) THEN d := HIGH(stkArg) END;
  1117. Cpy(funcName, stkName[d]);
  1118. rc := stkRes[d];
  1119. i := 0;
  1120. WHILE (stkArg[d][i] # CHR(0)) AND (i < HIGH(body)) DO
  1121. body[i] := stkArg[d][i]; INC(i)
  1122. END;
  1123. IF (i >= 2) AND (body[i-2] = ",") THEN
  1124. body[i-2] := CHR(0)
  1125. ELSE
  1126. body[i] := CHR(0)
  1127. END;
  1128. Revive;
  1129. IF wantRes THEN
  1130. NewTemp(q);
  1131. W(" "); W(q); W(" ="); Wc(rc)
  1132. ELSE
  1133. W(" ")
  1134. END;
  1135. IF stkInd[d] THEN
  1136. W(" call "); W(funcName); W("(") (* callee operand already %-prefixed *)
  1137. ELSE
  1138. W(" call $"); W(funcName); W("(")
  1139. END;
  1140. IF stkExt[d] THEN
  1141. W(body) (* C callee: no static link *)
  1142. ELSE
  1143. W("l "); W(stkLink[d]);
  1144. IF body[0] # CHR(0) THEN W(", "); W(body) END
  1145. END;
  1146. WL(")")
  1147. END CallEnd;
  1148. PROCEDURE InitStack (addr: ARRAY OF CHAR; t: INTEGER);
  1149. BEGIN
  1150. useStack := TRUE;
  1151. InitHeap(addr, t);
  1152. useStack := FALSE
  1153. END InitStack;
  1154. PROCEDURE ArgClass (t: INTEGER): CHAR;
  1155. (* Formal/actual class for call emission. *)
  1156. BEGIN
  1157. RETURN ResClass(t)
  1158. END ArgClass;
  1159. (* ---------------- temporaries and operators ---------------- *)
  1160. PROCEDURE NewTemp (VAR t: QVal);
  1161. BEGIN
  1162. Cpy(t, "%t");
  1163. AppNum(t, nTemp);
  1164. INC(nTemp)
  1165. END NewTemp;
  1166. PROCEDURE LocLoadFlat (idx: INTEGER; long: BOOLEAN; VAR q: QVal);
  1167. (* Loads a same-scope entry: slots/data by class (or long for
  1168. pointers), addr entries via ElemLoad. *)
  1169. BEGIN
  1170. IF locTag[idx] = 2 THEN
  1171. ElemLoad(locRep[idx], locTyp[idx], q);
  1172. RETURN
  1173. END;
  1174. NewTemp(q);
  1175. Revive;
  1176. W(" "); W(q);
  1177. IF long THEN W(" =l loadl ")
  1178. ELSIF locCls[idx] = "d" THEN W(" =d loadd ")
  1179. ELSE W(" =w loadw ")
  1180. END;
  1181. IF locTag[idx] = 1 THEN W("$") END;
  1182. WL(locRep[idx])
  1183. END LocLoadFlat;
  1184. PROCEDURE LocStoreFlat (idx: INTEGER; q: ARRAY OF CHAR; long: BOOLEAN);
  1185. (* Stores a same-scope entry: slots/data by class (or long), addr
  1186. via ElemStore. Data stores are grammar-unreachable (consts). *)
  1187. BEGIN
  1188. IF locTag[idx] = 2 THEN
  1189. ElemStore(locRep[idx], q, locTyp[idx]);
  1190. RETURN
  1191. END;
  1192. Revive;
  1193. IF long THEN W(" storel ")
  1194. ELSIF locCls[idx] = "d" THEN W(" stored ")
  1195. ELSE W(" storew ")
  1196. END;
  1197. W(q); W(", ");
  1198. IF locTag[idx] = 1 THEN W("$") END;
  1199. WL(locRep[idx])
  1200. END LocStoreFlat;
  1201. PROCEDURE UpLoad (flat: INTEGER; levels: CARDINAL; long: BOOLEAN;
  1202. VAR q: QVal);
  1203. (* Up-level load: data entries live globally; otherwise the link
  1204. cell yields the address and ElemLoad reads through it. *)
  1205. VAR a : QVal;
  1206. rel : CARDINAL;
  1207. BEGIN
  1208. IF locTag[flat] = 1 THEN
  1209. NewTemp(q);
  1210. Revive;
  1211. W(" "); W(q);
  1212. IF long THEN W(" =l loadl $")
  1213. ELSIF locCls[flat] = "d" THEN W(" =d loadd $")
  1214. ELSE W(" =w loadw $")
  1215. END;
  1216. WL(locRep[flat]);
  1217. RETURN
  1218. END;
  1219. rel := VAL(CARDINAL, flat) - scopeBase[scopeTop - 1 - levels];
  1220. UpAddr(levels, rel, a);
  1221. ElemLoad(a, locTyp[flat], q)
  1222. END UpLoad;
  1223. PROCEDURE UpStore (flat: INTEGER; levels: CARDINAL; q: ARRAY OF CHAR;
  1224. long: BOOLEAN);
  1225. VAR a : QVal;
  1226. rel : CARDINAL;
  1227. BEGIN
  1228. IF locTag[flat] = 1 THEN
  1229. Revive;
  1230. IF long THEN W(" storel ")
  1231. ELSIF locCls[flat] = "d" THEN W(" stored ")
  1232. ELSE W(" storew ")
  1233. END;
  1234. W(q); W(", $"); WL(locRep[flat]);
  1235. RETURN
  1236. END;
  1237. rel := VAL(CARDINAL, flat) - scopeBase[scopeTop - 1 - levels];
  1238. UpAddr(levels, rel, a);
  1239. ElemStore(a, q, locTyp[flat])
  1240. END UpStore;
  1241. PROCEDURE UpAddrOf (flat: INTEGER; levels: CARDINAL; VAR q: QVal);
  1242. (* Up-level address: data entries name globals; otherwise the
  1243. link cell holds it (composites) or points at it. For scalars
  1244. the cell IS the usable address (slot or actual address). *)
  1245. VAR a : QVal;
  1246. rel : CARDINAL;
  1247. BEGIN
  1248. IF locTag[flat] = 1 THEN
  1249. Cpy(q, "$");
  1250. App(q, locRep[flat]);
  1251. RETURN
  1252. END;
  1253. rel := VAL(CARDINAL, flat) - scopeBase[scopeTop - 1 - levels];
  1254. UpAddr(levels, rel, q)
  1255. END UpAddrOf;
  1256. PROCEDURE LoadDesignator (name: ARRAY OF CHAR; t: INTEGER; k: INTEGER;
  1257. VAR q: QVal): BOOLEAN;
  1258. VAR cls : INTEGER;
  1259. BEGIN
  1260. cls := SymTab.ClassOf(t);
  1261. IF SymTab.Equal(name, "TRUE") THEN
  1262. Cpy(q, "1"); RETURN TRUE
  1263. ELSIF SymTab.Equal(name, "FALSE") THEN
  1264. Cpy(q, "0"); RETURN TRUE
  1265. ELSIF SymTab.Equal(name, "NIL") THEN
  1266. Cpy(q, "0"); RETURN TRUE
  1267. END;
  1268. IF (k = SymTab.KindVar) OR (k = SymTab.KindParam)
  1269. OR (k = SymTab.KindConst) THEN
  1270. IF (cls = SymTab.ClInt) OR (cls = SymTab.ClBool)
  1271. OR (cls = SymTab.ClChar) OR (cls = SymTab.ClReal) THEN
  1272. LoadVar(name, cls = SymTab.ClReal, q); RETURN TRUE
  1273. ELSIF (cls = SymTab.ClPtr) OR (cls = SymTab.ClProc) THEN
  1274. LoadPtr(name, q); RETURN TRUE
  1275. ELSIF (cls = SymTab.ClArray) OR (cls = SymTab.ClSet)
  1276. OR (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  1277. AddrOf(name, q); RETURN TRUE
  1278. END
  1279. END;
  1280. Cpy(q, "0");
  1281. RETURN FALSE
  1282. END LoadDesignator;
  1283. PROCEDURE LoadVar (name: ARRAY OF CHAR; isReal: BOOLEAN; VAR q: QVal);
  1284. VAR idx : INTEGER;
  1285. levels : CARDINAL;
  1286. ref : QVal;
  1287. BEGIN
  1288. IF LocFindUp(name, levels, idx) THEN
  1289. IF levels = 0 THEN LocLoadFlat(idx, FALSE, q)
  1290. ELSE UpLoad(idx, levels, FALSE, q)
  1291. END;
  1292. RETURN
  1293. END;
  1294. NewTemp(q);
  1295. Revive;
  1296. SymRef(name, ref);
  1297. W(" "); W(q);
  1298. IF isReal THEN W(" =d loadd ") ELSE W(" =w loadw ") END;
  1299. WL(ref)
  1300. END LoadVar;
  1301. PROCEDURE LoadPtr (name: ARRAY OF CHAR; VAR q: QVal);
  1302. VAR idx : INTEGER;
  1303. levels : CARDINAL;
  1304. ref : QVal;
  1305. BEGIN
  1306. IF LocFindUp(name, levels, idx) THEN
  1307. IF levels = 0 THEN LocLoadFlat(idx, TRUE, q)
  1308. ELSE UpLoad(idx, levels, TRUE, q)
  1309. END;
  1310. RETURN
  1311. END;
  1312. NewTemp(q);
  1313. Revive;
  1314. SymRef(name, ref);
  1315. W(" "); W(q); W(" =l loadl ");
  1316. WL(ref)
  1317. END LoadPtr;
  1318. PROCEDURE LoadLong (name: ARRAY OF CHAR; VAR q: QVal);
  1319. VAR idx : INTEGER;
  1320. levels : CARDINAL;
  1321. ref : QVal;
  1322. BEGIN
  1323. IF LocFindUp(name, levels, idx) THEN
  1324. IF levels = 0 THEN LocLoadFlat(idx, TRUE, q)
  1325. ELSE UpLoad(idx, levels, TRUE, q)
  1326. END;
  1327. RETURN
  1328. END;
  1329. NewTemp(q);
  1330. Revive;
  1331. SymRef(name, ref);
  1332. W(" "); W(q); W(" =l loadl ");
  1333. WL(ref)
  1334. END LoadLong;
  1335. PROCEDURE StoreLong (name: ARRAY OF CHAR; q: ARRAY OF CHAR);
  1336. VAR idx : INTEGER;
  1337. levels : CARDINAL;
  1338. ref : QVal;
  1339. BEGIN
  1340. IF LocFindUp(name, levels, idx) THEN
  1341. IF levels = 0 THEN LocStoreFlat(idx, q, TRUE)
  1342. ELSE UpStore(idx, levels, q, TRUE)
  1343. END;
  1344. RETURN
  1345. END;
  1346. Revive;
  1347. SymRef(name, ref);
  1348. W(" storel "); W(q); W(", "); WL(ref)
  1349. END StoreLong;
  1350. PROCEDURE WidenLong (a: ARRAY OF CHAR; VAR q: QVal);
  1351. (* INTEGER (w) -> LONGINT (l) via extsw. *)
  1352. BEGIN
  1353. NewTemp(q);
  1354. Revive;
  1355. W(" "); W(q); W(" =l extsw "); WL(a)
  1356. END WidenLong;
  1357. PROCEDURE CmpLong (op: INTEGER; l, r: ARRAY OF CHAR; VAR q: QVal);
  1358. (* 64-bit comparison (result w). *)
  1359. VAR mn : ARRAY [0 .. 7] OF CHAR;
  1360. BEGIN
  1361. mn[0] := CHR(0);
  1362. IF op = SymTab.OpEq THEN Cpy(mn, "ceql")
  1363. ELSIF (op = SymTab.OpNeq1) OR (op = SymTab.OpNeq2) THEN Cpy(mn, "cnel")
  1364. ELSIF op = SymTab.OpLt THEN Cpy(mn, "csltl")
  1365. ELSIF op = SymTab.OpLe THEN Cpy(mn, "cslel")
  1366. ELSIF op = SymTab.OpGt THEN Cpy(mn, "csgtl")
  1367. ELSE Cpy(mn, "csgel")
  1368. END;
  1369. NewTemp(q);
  1370. Op3(mn, q, l, r, FALSE)
  1371. END CmpLong;
  1372. PROCEDURE StoreVar (name: ARRAY OF CHAR; q: ARRAY OF CHAR; isReal: BOOLEAN);
  1373. VAR idx : INTEGER;
  1374. levels : CARDINAL;
  1375. ref : QVal;
  1376. BEGIN
  1377. IF LocFindUp(name, levels, idx) THEN
  1378. IF levels = 0 THEN LocStoreFlat(idx, q, FALSE)
  1379. ELSE UpStore(idx, levels, q, FALSE)
  1380. END;
  1381. RETURN
  1382. END;
  1383. Revive;
  1384. SymRef(name, ref);
  1385. IF isReal THEN W(" stored ") ELSE W(" storew ") END;
  1386. W(q); W(", "); WL(ref)
  1387. END StoreVar;
  1388. PROCEDURE StorePtr (name: ARRAY OF CHAR; q: ARRAY OF CHAR);
  1389. VAR idx : INTEGER;
  1390. levels : CARDINAL;
  1391. ref : QVal;
  1392. BEGIN
  1393. IF LocFindUp(name, levels, idx) THEN
  1394. IF levels = 0 THEN LocStoreFlat(idx, q, TRUE)
  1395. ELSE UpStore(idx, levels, q, TRUE)
  1396. END;
  1397. RETURN
  1398. END;
  1399. Revive;
  1400. SymRef(name, ref);
  1401. W(" storel "); W(q); W(", "); WL(ref)
  1402. END StorePtr;
  1403. PROCEDURE Op3 (mn: ARRAY OF CHAR; res, l, r: ARRAY OF CHAR;
  1404. isReal: BOOLEAN);
  1405. BEGIN
  1406. Revive;
  1407. W(" "); W(res);
  1408. IF isReal THEN W(" =d ") ELSE W(" =w ") END;
  1409. W(mn); W(" "); W(l); W(", "); WL(r)
  1410. END Op3;
  1411. PROCEDURE AbsQ (a: ARRAY OF CHAR; VAR q: QVal; isReal: BOOLEAN);
  1412. VAR c, neg : QVal;
  1413. lNeg, lPos, lDone : QVal;
  1414. BEGIN
  1415. IF isReal THEN Cmp(SymTab.OpLt, a, "d_0.0", c, TRUE)
  1416. ELSE Cmp(SymTab.OpLt, a, "0", c, FALSE)
  1417. END;
  1418. NewLabel(lNeg); NewLabel(lPos); NewLabel(lDone);
  1419. NewTemp(neg);
  1420. NegQ(a, neg, isReal);
  1421. Jnz(c, lNeg, lPos);
  1422. EmitLabel(lNeg);
  1423. CopyOp(neg, q);
  1424. Jmp(lDone);
  1425. EmitLabel(lPos);
  1426. CopyOp(a, q);
  1427. EmitLabel(lDone)
  1428. END AbsQ;
  1429. PROCEDURE NegQ (a: ARRAY OF CHAR; VAR q: QVal; isReal: BOOLEAN);
  1430. BEGIN
  1431. NewTemp(q);
  1432. IF isReal THEN Op3("sub", q, "d_0.0", a, TRUE)
  1433. ELSE Op3("sub", q, "0", a, FALSE)
  1434. END
  1435. END NegQ;
  1436. PROCEDURE ConvIR (a: ARRAY OF CHAR; VAR q: QVal);
  1437. (* INTEGER -> REAL widening for assignments (swtof). *)
  1438. BEGIN
  1439. NewTemp(q);
  1440. Revive;
  1441. W(" "); W(q); W(" =d swtof "); WL(a)
  1442. END ConvIR;
  1443. PROCEDURE NarrowLong (a: ARRAY OF CHAR; VAR q: QVal);
  1444. (* LONGINT (l) -> INTEGER (w): keep the low 32 bits. *)
  1445. BEGIN
  1446. NewTemp(q);
  1447. Revive;
  1448. W(" "); W(q); W(" =w copy "); WL(a)
  1449. END NarrowLong;
  1450. PROCEDURE ConvRI (a: ARRAY OF CHAR; VAR q: QVal);
  1451. (* REAL -> INTEGER (truncate toward zero). *)
  1452. BEGIN
  1453. NewTemp(q);
  1454. Revive;
  1455. W(" "); W(q); W(" =w dtosi "); WL(a)
  1456. END ConvRI;
  1457. PROCEDURE ConvRL (a: ARRAY OF CHAR; VAR q: QVal);
  1458. (* REAL -> LONGINT (truncate toward zero). *)
  1459. BEGIN
  1460. NewTemp(q);
  1461. Revive;
  1462. W(" "); W(q); W(" =l dtosi "); WL(a)
  1463. END ConvRL;
  1464. PROCEDURE ConvLR (a: ARRAY OF CHAR; VAR q: QVal);
  1465. (* LONGINT -> REAL. *)
  1466. BEGIN
  1467. NewTemp(q);
  1468. Revive;
  1469. W(" "); W(q); W(" =d sltof "); WL(a)
  1470. END ConvLR;
  1471. PROCEDURE NotQ (a: ARRAY OF CHAR; VAR q: QVal);
  1472. BEGIN
  1473. NewTemp(q);
  1474. Op3("xor", q, "1", a, FALSE)
  1475. END NotQ;
  1476. PROCEDURE CapQ (a: ARRAY OF CHAR; VAR q: QVal);
  1477. (* Branchless UPCASE: q := a - 32 * (a >= 'a' AND a <= 'z'). *)
  1478. VAR lo, hi, in1, delta: QVal;
  1479. BEGIN
  1480. NewTemp(lo); Op3("csgew", lo, a, "97", FALSE);
  1481. NewTemp(hi); Op3("cslew", hi, a, "122", FALSE);
  1482. NewTemp(in1); Op3("and", in1, lo, hi, FALSE);
  1483. NewTemp(delta); Op3("mul", delta, in1, "32", FALSE);
  1484. NewTemp(q); Op3("sub", q, a, delta, FALSE)
  1485. END CapQ;
  1486. PROCEDURE NewLabel (VAR l: QVal);
  1487. BEGIN
  1488. Cpy(l, "@L");
  1489. AppNum(l, nLab);
  1490. INC(nLab)
  1491. END NewLabel;
  1492. PROCEDURE Revive;
  1493. (* Opens an unreachable block if past a terminator. Keeps every
  1494. temporary defined and every instruction inside a block, even for
  1495. dead source tails (EXIT followed by more statements) and CASE
  1496. chains whose compares follow a Jmp. Counter-driven: fixpoint-safe. *)
  1497. VAR b: QVal;
  1498. BEGIN
  1499. IF opened AND dead THEN
  1500. NewLabel(b);
  1501. WL(b);
  1502. dead := FALSE
  1503. END
  1504. END Revive;
  1505. PROCEDURE EmitLabel (l: ARRAY OF CHAR);
  1506. BEGIN
  1507. WL(l);
  1508. dead := FALSE
  1509. END EmitLabel;
  1510. PROCEDURE Jmp (l: ARRAY OF CHAR);
  1511. BEGIN
  1512. IF dead THEN RETURN END; (* unreachable: a terminator with no
  1513. intervening label can never be reached; emitting it would end
  1514. the block and orphan whatever follows. *)
  1515. W(" jmp "); WL(l);
  1516. dead := TRUE
  1517. END Jmp;
  1518. PROCEDURE Jnz (c, t, f: ARRAY OF CHAR);
  1519. BEGIN
  1520. IF dead THEN RETURN END;
  1521. W(" jnz "); W(c); W(", "); W(t); W(", "); WL(f);
  1522. dead := TRUE
  1523. END Jnz;
  1524. PROCEDURE Cmp (op: INTEGER; l, r: ARRAY OF CHAR; VAR q: QVal;
  1525. isReal: BOOLEAN);
  1526. VAR mn : ARRAY [0 .. 7] OF CHAR;
  1527. BEGIN
  1528. mn[0] := CHR(0);
  1529. IF isReal THEN
  1530. IF op = SymTab.OpEq THEN Cpy(mn, "ceqd")
  1531. ELSIF (op = SymTab.OpNeq1) OR (op = SymTab.OpNeq2) THEN Cpy(mn, "cned")
  1532. ELSIF op = SymTab.OpLt THEN Cpy(mn, "cltd")
  1533. ELSIF op = SymTab.OpLe THEN Cpy(mn, "cled")
  1534. ELSIF op = SymTab.OpGt THEN Cpy(mn, "cgtd")
  1535. ELSE Cpy(mn, "cged")
  1536. END
  1537. ELSE
  1538. IF op = SymTab.OpEq THEN Cpy(mn, "ceqw")
  1539. ELSIF (op = SymTab.OpNeq1) OR (op = SymTab.OpNeq2) THEN Cpy(mn, "cnew")
  1540. ELSIF op = SymTab.OpLt THEN Cpy(mn, "csltw")
  1541. ELSIF op = SymTab.OpLe THEN Cpy(mn, "cslew")
  1542. ELSIF op = SymTab.OpGt THEN Cpy(mn, "csgtw")
  1543. ELSE Cpy(mn, "csgew")
  1544. END
  1545. END;
  1546. NewTemp(q);
  1547. Op3(mn, q, l, r, FALSE)
  1548. END Cmp;
  1549. PROCEDURE StrEq (op: INTEGER; l, r: ARRAY OF CHAR; VAR q: QVal);
  1550. (* String equality via the shim: q := (l = r) as a BOOLEAN, negated for
  1551. OpNeq. Operands are descriptor addresses; the shim strcmps the
  1552. NUL-terminated contents. *)
  1553. VAR t: QVal;
  1554. BEGIN
  1555. NewTemp(q);
  1556. W(" "); W(q); W(" =w call $m2streq(l ");
  1557. W(l); W(", l "); W(r); WL(")");
  1558. IF (op = SymTab.OpNeq1) OR (op = SymTab.OpNeq2) THEN
  1559. NewTemp(t);
  1560. W(" "); W(t); W(" =w xor "); W(q); WL(", 1");
  1561. Cpy(q, t)
  1562. END
  1563. END StrEq;
  1564. PROCEDURE StrLen (s: ARRAY OF CHAR; VAR q: QVal);
  1565. (* q := the number of characters before the NUL (INTEGER). *)
  1566. BEGIN
  1567. NewTemp(q);
  1568. W(" "); W(q); W(" =w call $m2strlen(l "); W(s); WL(")")
  1569. END StrLen;
  1570. PROCEDURE StrAssign (dst, src: ARRAY OF CHAR);
  1571. (* Copy the string src into the CHAR array at dst (content copy,
  1572. truncated to the destination capacity, NUL-terminated). *)
  1573. BEGIN
  1574. W(" call $m2strassign(l "); W(dst); W(", l "); W(src); WL(")")
  1575. END StrAssign;
  1576. PROCEDURE UAssign (dst, src: ARRAY OF CHAR);
  1577. (* Copy a U"..." descriptor (count + 4-byte codepoints) into the UCHAR
  1578. array at dst: min(count, capacity) codepoints + a 0 terminator. *)
  1579. BEGIN
  1580. W(" call $m2uassign(l "); W(dst); W(", l "); W(src); WL(")")
  1581. END UAssign;
  1582. PROCEDURE UStrLen (s: ARRAY OF CHAR; VAR q: QVal);
  1583. (* q := the UString codepoint count (INTEGER). *)
  1584. VAR t: QVal;
  1585. BEGIN
  1586. NewTemp(t);
  1587. W(" "); W(t); W(" =w call $m2ustrlen(l "); W(s); WL(")");
  1588. Cpy(q, t)
  1589. END UStrLen;
  1590. PROCEDURE DecQ (VAR q: QVal);
  1591. (* q := q - 1 (w domain; q is an INTEGER count). *)
  1592. VAR t: QVal;
  1593. BEGIN
  1594. NewTemp(t);
  1595. Op3("sub", t, q, "1", FALSE);
  1596. Cpy(q, t)
  1597. END DecQ;
  1598. PROCEDURE UStrCat (a, b: ARRAY OF CHAR; VAR q: QVal);
  1599. (* q := a + b (a UString descriptor in the shim's concat buffer). *)
  1600. BEGIN
  1601. NewTemp(q);
  1602. W(" "); W(q); W(" =l call $m2ustrcat(l "); W(a); W(", l "); W(b); WL(")")
  1603. END UStrCat;
  1604. PROCEDURE UStrFrom (cp: ARRAY OF CHAR; VAR q: QVal);
  1605. (* q := a 1-codepoint UString descriptor for the UCHAR value cp. *)
  1606. BEGIN
  1607. NewTemp(q);
  1608. W(" "); W(q); W(" =l call $m2ustrfrom(l "); W(cp); WL(")")
  1609. END UStrFrom;
  1610. PROCEDURE StrCat (a, b: ARRAY OF CHAR; VAR q: QVal);
  1611. (* q := a + b (a descriptor in the shim's concat buffer). *)
  1612. BEGIN
  1613. NewTemp(q);
  1614. W(" "); W(q); W(" =l call $m2strcat(l "); W(a); W(", l "); W(b); WL(")")
  1615. END StrCat;
  1616. (* ---------------- arrays: length-prefixed layout ---------------- *)
  1617. (* Indexes and counts are LONGCARD (l) in emitted code; immediates
  1618. pass through, w-temps widen via extsw. Traps call $abort + hlt
  1619. (interim; runtime/syslib/Trap replaces them later). *)
  1620. PROCEDURE ElemCls (t: SymTab.TypeIndex): INTEGER;
  1621. BEGIN
  1622. RETURN SymTab.ClassOf(SymTab.ArrayElem(t))
  1623. END ElemCls;
  1624. PROCEDURE ElemSize (t: SymTab.TypeIndex): CARDINAL;
  1625. (* Storage size of t's elements: CHAR 1, REAL 8, nested/pointer 8,
  1626. else 4. t is an array (or string) descriptor. *)
  1627. VAR cls: INTEGER;
  1628. BEGIN
  1629. cls := SymTab.ClassOf(t);
  1630. IF cls = SymTab.ClStr THEN RETURN 1 END;
  1631. cls := ElemCls(t);
  1632. IF cls = SymTab.ClChar THEN RETURN 1
  1633. ELSIF cls = SymTab.ClReal THEN RETURN 8
  1634. ELSIF (cls = SymTab.ClArray) OR (cls = SymTab.ClPtr)
  1635. OR (cls = SymTab.ClLong) THEN RETURN 8
  1636. ELSE RETURN 4
  1637. END
  1638. END ElemSize;
  1639. PROCEDURE Op3L (mn: ARRAY OF CHAR; res, l, r: ARRAY OF CHAR);
  1640. BEGIN
  1641. Revive;
  1642. W(" "); W(res);
  1643. W(" =l ");
  1644. W(mn); W(" "); W(l); W(", "); WL(r)
  1645. END Op3L;
  1646. PROCEDURE HaltQ;
  1647. BEGIN
  1648. Revive;
  1649. WL(" call $exit(w 1)");
  1650. WL(" hlt");
  1651. dead := TRUE
  1652. END HaltQ;
  1653. PROCEDURE Trap;
  1654. BEGIN
  1655. Revive;
  1656. WL(" call $abort()");
  1657. WL(" hlt");
  1658. dead := TRUE
  1659. END Trap;
  1660. (* ---------------- records: flat blobs, pointer fields ---------------- *)
  1661. (* Scalars/sets inline, array fields as 8-byte pointers to static
  1662. descriptors, nested records inline. Static offsets throughout. *)
  1663. PROCEDURE ArrBodyItems (prefix: ARRAY OF CHAR; t: INTEGER);
  1664. (* Inline contents of an array descriptor (no "data $name = {" wrapper
  1665. and no closing brace): "l <n>[, <elem>...]". Nested levels are
  1666. referenced as $prefix_i (emitted by ArrData). *)
  1667. VAR n, i: CARDINAL;
  1668. elem: SymTab.TypeIndex;
  1669. ecls: INTEGER;
  1670. esz: CARDINAL;
  1671. bv: QVal;
  1672. BEGIN
  1673. n := SymTab.ArrayLen(t);
  1674. elem := SymTab.ArrayElem(t);
  1675. ecls := SymTab.ClassOf(elem);
  1676. W("l ");
  1677. IntStr(VAL(INTEGER, n), bv);
  1678. W(bv);
  1679. IF ecls = SymTab.ClArray THEN
  1680. i := 0;
  1681. WHILE i < n DO
  1682. W(", l $"); W(prefix); W("_");
  1683. IntStr(VAL(INTEGER, i), bv);
  1684. W(bv);
  1685. INC(i)
  1686. END
  1687. ELSE
  1688. IF ecls = SymTab.ClChar THEN
  1689. (* CHAR: n data bytes + one NUL terminator slot *)
  1690. W(", z ");
  1691. IntStr(VAL(INTEGER, n + 1), bv);
  1692. W(bv)
  1693. ELSIF ecls = SymTab.ClUChar THEN
  1694. (* UCHAR: n codepoints + one 0-codepoint terminator slot *)
  1695. W(", z ");
  1696. IntStr(VAL(INTEGER, (n + 1) * 4), bv);
  1697. W(bv)
  1698. ELSE
  1699. IF (ecls = SymTab.ClReal) OR (ecls = SymTab.ClPtr)
  1700. OR (ecls = SymTab.ClProc) THEN esz := 8
  1701. ELSE esz := 4
  1702. END;
  1703. IF n > 0 THEN
  1704. W(", z ");
  1705. IntStr(VAL(INTEGER, n * esz), bv);
  1706. W(bv)
  1707. END
  1708. END
  1709. END
  1710. END ArrBodyItems;
  1711. PROCEDURE ArrData (name: ARRAY OF CHAR; t: INTEGER);
  1712. (* "data $name = { <items> }" plus, for nested levels, the recursive
  1713. sub-descriptor data $name_i. *)
  1714. VAR n, i: CARDINAL;
  1715. elem: SymTab.TypeIndex;
  1716. bv: QVal;
  1717. sub: QVal;
  1718. BEGIN
  1719. IF NOT opened THEN RETURN END;
  1720. W("data $"); W(name);
  1721. W(" = { ");
  1722. ArrBodyItems(name, t);
  1723. WL(" }");
  1724. elem := SymTab.ArrayElem(t);
  1725. IF SymTab.ClassOf(elem) = SymTab.ClArray THEN
  1726. n := SymTab.ArrayLen(t);
  1727. i := 0;
  1728. WHILE i < n DO
  1729. Cpy(sub, name); App(sub, "_");
  1730. IntStr(VAL(INTEGER, i), bv);
  1731. App(sub, bv);
  1732. ArrData(sub, elem);
  1733. INC(i)
  1734. END
  1735. END
  1736. END ArrData;
  1737. PROCEDURE RecStatics (recname: ARRAY OF CHAR; t: SymTab.TypeIndex);
  1738. (* Pre-pass: static sub-descriptors for NESTED array fields (the
  1739. top-level array field is now inline in the record), recursive. *)
  1740. VAR n, i, k, an: CARDINAL;
  1741. fn: SymTab.Name;
  1742. ft, et: SymTab.TypeIndex;
  1743. cls: INTEGER;
  1744. sub, bv: QVal;
  1745. BEGIN
  1746. n := SymTab.FieldCount(t);
  1747. i := 0;
  1748. WHILE i < n DO
  1749. SymTab.FieldName(t, i, fn);
  1750. ft := SymTab.FieldType(t, fn);
  1751. cls := SymTab.ClassOf(ft);
  1752. IF cls = SymTab.ClArray THEN
  1753. et := SymTab.ArrayElem(ft);
  1754. IF SymTab.ClassOf(et) = SymTab.ClArray THEN
  1755. an := SymTab.ArrayLen(ft);
  1756. k := 0;
  1757. WHILE k < an DO
  1758. Cpy(sub, recname); App(sub, "_"); App(sub, fn);
  1759. App(sub, "_");
  1760. IntStr(VAL(INTEGER, k), bv);
  1761. App(sub, bv);
  1762. ArrData(sub, et);
  1763. INC(k)
  1764. END
  1765. END
  1766. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  1767. Cpy(sub, recname); App(sub, "_"); App(sub, fn);
  1768. RecStatics(sub, ft)
  1769. END;
  1770. INC(i)
  1771. END
  1772. END RecStatics;
  1773. PROCEDURE RecItems (t: SymTab.TypeIndex; prefix: ARRAY OF CHAR;
  1774. VAR first: BOOLEAN);
  1775. (* Comma-separated item list (no wrapper); nested records inline.
  1776. Emission order follows the (prepend-built) field chain, i.e.
  1777. reverse declaration; offsets (not positions) place everything. *)
  1778. VAR n, i, k, w: CARDINAL;
  1779. fn: SymTab.Name;
  1780. ft: SymTab.TypeIndex;
  1781. cls: INTEGER;
  1782. sub: QVal;
  1783. PROCEDURE Sep;
  1784. BEGIN
  1785. IF first THEN first := FALSE ELSE W(", ") END
  1786. END Sep;
  1787. BEGIN
  1788. n := SymTab.FieldCount(t);
  1789. i := 0;
  1790. WHILE i < n DO
  1791. SymTab.FieldName(t, i, fn);
  1792. ft := SymTab.FieldType(t, fn);
  1793. cls := SymTab.ClassOf(ft);
  1794. IF cls = SymTab.ClReal THEN Sep; W("d 0")
  1795. ELSIF cls = SymTab.ClChar THEN Sep; W("b 0")
  1796. ELSIF cls = SymTab.ClArray THEN
  1797. (* array field: inline descriptor (was a pointer) *)
  1798. Sep;
  1799. Cpy(sub, prefix); App(sub, "_"); App(sub, fn);
  1800. ArrBodyItems(sub, ft)
  1801. ELSIF cls = SymTab.ClSet THEN
  1802. w := SymTab.SetWords(ft);
  1803. IF w = 0 THEN w := 1 END;
  1804. Sep; W("w 0");
  1805. k := 1;
  1806. WHILE k < w DO W(", w 0"); INC(k) END
  1807. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  1808. Cpy(sub, prefix); App(sub, "_"); App(sub, fn);
  1809. RecItems(ft, sub, first)
  1810. ELSE Sep; W("w 0")
  1811. END;
  1812. INC(i)
  1813. END
  1814. END RecItems;
  1815. PROCEDURE VtRef (t: INTEGER; VAR q: QVal);
  1816. (* The class's vtable symbol ($vt_<typeindex>). *)
  1817. VAR bv: QVal;
  1818. BEGIN
  1819. Cpy(q, "$vt_");
  1820. IntStr(t, bv);
  1821. App(q, bv)
  1822. END VtRef;
  1823. PROCEDURE EmitVTables;
  1824. (* One data array per class with a vtable: slots (inherited first) as
  1825. method code addresses. *)
  1826. VAR k, j, n: CARDINAL;
  1827. t: INTEGER;
  1828. nm: SymTab.Name;
  1829. mg, vt: QVal;
  1830. BEGIN
  1831. IF NOT opened THEN RETURN END;
  1832. k := 0;
  1833. WHILE k < SymTab.VtClassCount() DO
  1834. t := SymTab.VtClassAt(k);
  1835. n := SymTab.VtCount(t);
  1836. VtRef(t, vt);
  1837. W("data "); W(vt); W(" = { ");
  1838. j := 0;
  1839. WHILE j < n DO
  1840. IF SymTab.VtName(t, j, nm) THEN
  1841. IF j > 0 THEN W(", ") END;
  1842. IF SymTab.VtHasBody(t, j) THEN
  1843. Mangled(nm, SymTab.VtUid(t, j), mg);
  1844. W("l $"); W(mg)
  1845. ELSE
  1846. W("l 0") (* declared but never implemented *)
  1847. END
  1848. END;
  1849. INC(j)
  1850. END;
  1851. IF n = 0 THEN W("l 0") END;
  1852. WL(" }");
  1853. INC(k)
  1854. END
  1855. END EmitVTables;
  1856. PROCEDURE DeclRec (name: ARRAY OF CHAR; t: INTEGER);
  1857. VAR first: BOOLEAN;
  1858. vt: QVal;
  1859. BEGIN
  1860. IF NOT opened THEN RETURN END;
  1861. RecStatics(name, t);
  1862. W("data $"); W(name);
  1863. W(" = { ");
  1864. first := TRUE;
  1865. (* a class with a vtable stores its vtable pointer at offset 0 *)
  1866. IF (SymTab.ClassOf(t) = SymTab.ClClass) AND SymTab.HasVTable(t) THEN
  1867. VtRef(t, vt);
  1868. W("l "); W(vt);
  1869. first := FALSE
  1870. END;
  1871. RecItems(t, name, first);
  1872. IF first THEN W("w 0") END;
  1873. WL(" }")
  1874. END DeclRec;
  1875. PROCEDURE FieldAddr (base: ARRAY OF CHAR; off: INTEGER; VAR q: QVal);
  1876. VAR sb: QVal;
  1877. BEGIN
  1878. NewTemp(q);
  1879. IntStr(off, sb);
  1880. Op3L("add", q, base, sb)
  1881. END FieldAddr;
  1882. PROCEDURE ThisBase (VAR q: QVal);
  1883. (* The current method's receiver address (the hidden THIS VAR param). *)
  1884. BEGIN
  1885. AddrOf("THIS", q)
  1886. END ThisBase;
  1887. PROCEDURE PushWith (base: ARRAY OF CHAR);
  1888. BEGIN
  1889. IF withTop <= HIGH(withSt) THEN
  1890. Cpy(withSt[withTop], base); INC(withTop)
  1891. END
  1892. END PushWith;
  1893. PROCEDURE PopWith;
  1894. BEGIN
  1895. IF withTop > 0 THEN DEC(withTop) END
  1896. END PopWith;
  1897. PROCEDURE TopWith (VAR base: QVal): BOOLEAN;
  1898. BEGIN
  1899. IF withTop = 0 THEN RETURN FALSE END;
  1900. Cpy(base, withSt[withTop - 1]);
  1901. RETURN TRUE
  1902. END TopWith;
  1903. PROCEDURE CopyRecord (dst: ARRAY OF CHAR; src: ARRAY OF CHAR;
  1904. t: INTEGER);
  1905. (* Deep whole-record copy: scalars/sets by load/store, arrays via
  1906. CopyArray on dereferenced fields, nested records recurse. *)
  1907. VAR n, i: CARDINAL;
  1908. fn: SymTab.Name;
  1909. ft: SymTab.TypeIndex;
  1910. cls: INTEGER;
  1911. dd, ds, di, si, v: QVal;
  1912. off: QVal;
  1913. BEGIN
  1914. n := SymTab.FieldCount(t);
  1915. i := 0;
  1916. WHILE i < n DO
  1917. SymTab.FieldName(t, i, fn);
  1918. ft := SymTab.FieldType(t, fn);
  1919. cls := SymTab.ClassOf(ft);
  1920. IntStr(SymTab.FieldOffset(t, fn), off);
  1921. NewTemp(dd); Op3L("add", dd, dst, off);
  1922. NewTemp(ds); Op3L("add", ds, src, off);
  1923. IF cls = SymTab.ClArray THEN
  1924. (* inline array field: the field address is the descriptor *)
  1925. CopyArray(dd, ds, ft)
  1926. ELSIF cls = SymTab.ClSet THEN
  1927. CopySet(dd, ds, SymTab.SetWords(ft), SymTab.SetWords(ft))
  1928. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  1929. CopyRecord(dd, ds, ft)
  1930. ELSE
  1931. ElemLoad(ds, ft, v);
  1932. ElemStore(dd, v, ft)
  1933. END;
  1934. INC(i)
  1935. END
  1936. END CopyRecord;
  1937. PROCEDURE WordsBytes (words: CARDINAL): CARDINAL;
  1938. BEGIN
  1939. IF words = 0 THEN RETURN 8 END;
  1940. RETURN ((words * 4 + 7) DIV 8) * 8
  1941. END WordsBytes;
  1942. PROCEDURE DeclSet (name: ARRAY OF CHAR; t: INTEGER);
  1943. VAR w, i: CARDINAL;
  1944. BEGIN
  1945. IF NOT opened THEN RETURN END;
  1946. w := SymTab.SetWords(t);
  1947. IF w = 0 THEN w := 1 END;
  1948. W("data $"); W(name);
  1949. W(" = { w 0");
  1950. i := 1;
  1951. WHILE i < w DO
  1952. W(", w 0");
  1953. INC(i)
  1954. END;
  1955. WL(" }")
  1956. END DeclSet;
  1957. PROCEDURE NewSetTemp (words: CARDINAL; VAR q: QVal);
  1958. VAR nb: QVal;
  1959. BEGIN
  1960. NewTemp(q);
  1961. Revive;
  1962. IntStr(VAL(INTEGER, WordsBytes(words)), nb);
  1963. W(" "); W(q); W(" =l alloc8 "); WL(nb)
  1964. END NewSetTemp;
  1965. PROCEDURE SetZero (addr: ARRAY OF CHAR; words: CARDINAL);
  1966. VAR i: CARDINAL;
  1967. a, z, off: QVal;
  1968. BEGIN
  1969. NewTemp(z);
  1970. Op3("xor", z, "0", "0", FALSE);
  1971. i := 0;
  1972. WHILE i < words DO
  1973. NewTemp(a);
  1974. IntStr(VAL(INTEGER, i * 4), off);
  1975. Op3L("add", a, addr, off);
  1976. Revive;
  1977. W(" storew "); W(z); W(", "); WL(a);
  1978. INC(i)
  1979. END
  1980. END SetZero;
  1981. PROCEDURE SetBit (addr: ARRAY OF CHAR; val: ARRAY OF CHAR;
  1982. lo: INTEGER; span: CARDINAL);
  1983. (* ORs one element in: off = val - lo trapped in [0, span). *)
  1984. VAR off, offL, hiS, loS: QVal;
  1985. wi, bi, wil, off4, wa, wcur, m, wn: QVal;
  1986. BEGIN
  1987. NewTemp(off);
  1988. IntStr(lo, loS);
  1989. Op3("sub", off, val, loS, FALSE);
  1990. WidenIndex(off, offL);
  1991. IntStr(VAL(INTEGER, span) - 1, hiS);
  1992. CheckRange(offL, "0", hiS);
  1993. NewTemp(wi);
  1994. Op3("shr", wi, off, "5", FALSE);
  1995. NewTemp(bi);
  1996. Op3("and", bi, off, "31", FALSE);
  1997. WidenIndex(wi, wil);
  1998. NewTemp(off4);
  1999. Op3L("mul", off4, wil, "4");
  2000. NewTemp(wa);
  2001. Op3L("add", wa, addr, off4);
  2002. NewTemp(m);
  2003. Op3("shl", m, "1", bi, FALSE);
  2004. NewTemp(wcur);
  2005. W(" "); W(wcur); W(" =w loadw "); WL(wa);
  2006. NewTemp(wn);
  2007. Op3("or", wn, wcur, m, FALSE);
  2008. Revive;
  2009. W(" storew "); W(wn); W(", "); WL(wa)
  2010. END SetBit;
  2011. PROCEDURE SetRange (addr: ARRAY OF CHAR; a: ARRAY OF CHAR;
  2012. b: ARRAY OF CHAR; lo: INTEGER; span: CARDINAL);
  2013. VAR al, bl, hiS: QVal;
  2014. cur, c: QVal;
  2015. ltop, lbody, lend: QVal;
  2016. BEGIN
  2017. WidenIndex(a, al);
  2018. WidenIndex(b, bl);
  2019. IntStr(VAL(INTEGER, span) - 1, hiS);
  2020. CheckRange(al, "0", hiS);
  2021. CheckRange(bl, "0", hiS);
  2022. NewTemp(cur);
  2023. Op3("add", cur, a, "0", FALSE);
  2024. NewLabel(ltop); NewLabel(lbody); NewLabel(lend);
  2025. EmitLabel(ltop);
  2026. NewTemp(c);
  2027. Op3("cslew", c, cur, b, FALSE);
  2028. Jnz(c, lbody, lend);
  2029. EmitLabel(lbody);
  2030. SetBit(addr, cur, lo, span);
  2031. Op3("add", cur, cur, "1", FALSE);
  2032. Jmp(ltop);
  2033. EmitLabel(lend)
  2034. END SetRange;
  2035. PROCEDURE SetBinOp (sel: INTEGER; l: ARRAY OF CHAR; r: ARRAY OF CHAR;
  2036. lw, rw: CARDINAL; VAR q: QVal);
  2037. (* Union/intersection/difference/symdiff over possibly different
  2038. spans: overlap via op, larger-side extras copied (union/symdiff/
  2039. left-diff) or zeroed. Result has max words. *)
  2040. VAR i, m, res: CARDINAL;
  2041. la, ra, ta, a, b, c, nb, z: QVal;
  2042. BEGIN
  2043. m := lw;
  2044. IF rw < m THEN m := rw END;
  2045. res := lw;
  2046. IF rw > res THEN res := rw END;
  2047. NewSetTemp(res, q);
  2048. NewTemp(z);
  2049. Op3("xor", z, "0", "0", FALSE);
  2050. i := 0;
  2051. WHILE i < m DO
  2052. IntStr(VAL(INTEGER, i * 4), nb);
  2053. NewTemp(la); Op3L("add", la, l, nb);
  2054. NewTemp(ra); Op3L("add", ra, r, nb);
  2055. NewTemp(ta); Op3L("add", ta, q, nb);
  2056. NewTemp(a);
  2057. W(" "); W(a); W(" =w loadw "); WL(la);
  2058. NewTemp(b);
  2059. W(" "); W(b); W(" =w loadw "); WL(ra);
  2060. NewTemp(c);
  2061. IF sel = 0 THEN Op3("or", c, a, b, FALSE)
  2062. ELSIF sel = 1 THEN Op3("and", c, a, b, FALSE)
  2063. ELSIF sel = 2 THEN
  2064. NewTemp(nb);
  2065. Op3("xor", nb, b, "-1", FALSE);
  2066. Op3("and", c, a, nb, FALSE)
  2067. ELSE Op3("xor", c, a, b, FALSE)
  2068. END;
  2069. Revive;
  2070. W(" storew "); W(c); W(", "); WL(ta);
  2071. INC(i)
  2072. END;
  2073. WHILE i < lw DO
  2074. IntStr(VAL(INTEGER, i * 4), nb);
  2075. NewTemp(la); Op3L("add", la, l, nb);
  2076. NewTemp(ta); Op3L("add", ta, q, nb);
  2077. IF (sel = 0) OR (sel = 2) OR (sel = 3) THEN
  2078. NewTemp(a);
  2079. W(" "); W(a); W(" =w loadw "); WL(la);
  2080. Revive;
  2081. W(" storew "); W(a); W(", "); WL(ta)
  2082. ELSE
  2083. Revive;
  2084. W(" storew "); W(z); W(", "); WL(ta)
  2085. END;
  2086. INC(i)
  2087. END;
  2088. WHILE i < rw DO
  2089. IntStr(VAL(INTEGER, i * 4), nb);
  2090. NewTemp(ra); Op3L("add", ra, r, nb);
  2091. NewTemp(ta); Op3L("add", ta, q, nb);
  2092. IF (sel = 0) OR (sel = 3) THEN
  2093. NewTemp(b);
  2094. W(" "); W(b); W(" =w loadw "); WL(ra);
  2095. Revive;
  2096. W(" storew "); W(b); W(", "); WL(ta)
  2097. ELSE
  2098. Revive;
  2099. W(" storew "); W(z); W(", "); WL(ta)
  2100. END;
  2101. INC(i)
  2102. END
  2103. END SetBinOp;
  2104. PROCEDURE CmpSet (op: INTEGER; l: ARRAY OF CHAR; r: ARRAY OF CHAR;
  2105. lw, rw: CARDINAL; VAR q: QVal);
  2106. VAR i, m: CARDINAL;
  2107. la, ra, a, b, c, acc, nb: QVal;
  2108. BEGIN
  2109. NewTemp(acc);
  2110. Op3("xor", acc, "1", "0", FALSE);
  2111. m := lw;
  2112. IF rw < m THEN m := rw END;
  2113. i := 0;
  2114. WHILE i < m DO
  2115. IntStr(VAL(INTEGER, i * 4), nb);
  2116. NewTemp(la); Op3L("add", la, l, nb);
  2117. NewTemp(ra); Op3L("add", ra, r, nb);
  2118. NewTemp(a);
  2119. W(" "); W(a); W(" =w loadw "); WL(la);
  2120. NewTemp(b);
  2121. W(" "); W(b); W(" =w loadw "); WL(ra);
  2122. NewTemp(c);
  2123. Op3("ceqw", c, a, b, FALSE);
  2124. NewTemp(q);
  2125. Op3("and", q, acc, c, FALSE);
  2126. CopyOp(q, acc);
  2127. INC(i)
  2128. END;
  2129. WHILE i < lw DO
  2130. IntStr(VAL(INTEGER, i * 4), nb);
  2131. NewTemp(la); Op3L("add", la, l, nb);
  2132. NewTemp(a);
  2133. W(" "); W(a); W(" =w loadw "); WL(la);
  2134. NewTemp(c);
  2135. Op3("ceqw", c, a, "0", FALSE);
  2136. NewTemp(q);
  2137. Op3("and", q, acc, c, FALSE);
  2138. CopyOp(q, acc);
  2139. INC(i)
  2140. END;
  2141. WHILE i < rw DO
  2142. IntStr(VAL(INTEGER, i * 4), nb);
  2143. NewTemp(ra); Op3L("add", ra, r, nb);
  2144. NewTemp(b);
  2145. W(" "); W(b); W(" =w loadw "); WL(ra);
  2146. NewTemp(c);
  2147. Op3("ceqw", c, b, "0", FALSE);
  2148. NewTemp(q);
  2149. Op3("and", q, acc, c, FALSE);
  2150. CopyOp(q, acc);
  2151. INC(i)
  2152. END;
  2153. IF (op = SymTab.OpNeq1) OR (op = SymTab.OpNeq2) THEN
  2154. NewTemp(q);
  2155. Op3("xor", q, acc, "1", FALSE)
  2156. ELSE
  2157. CopyOp(acc, q)
  2158. END
  2159. END CmpSet;
  2160. PROCEDURE CopySet (dst: ARRAY OF CHAR; src: ARRAY OF CHAR;
  2161. dw, sw: CARDINAL);
  2162. (* Copies min words via memcpy, zero-fills dst extras. Src extras
  2163. beyond dst must be zero (trap) — otherwise out-of-span bits
  2164. would vanish silently on narrowing assignment. *)
  2165. VAR m, i: CARDINAL;
  2166. n, da, sa, a, z, off, c: QVal;
  2167. qr, acc, lok, lbad: QVal;
  2168. BEGIN
  2169. m := dw;
  2170. IF sw < m THEN m := sw END;
  2171. IF sw > dw THEN
  2172. NewTemp(acc);
  2173. Op3("xor", acc, "1", "0", FALSE);
  2174. i := dw;
  2175. WHILE i < sw DO
  2176. IntStr(VAL(INTEGER, i * 4), off);
  2177. NewTemp(a); Op3L("add", a, src, off);
  2178. NewTemp(c);
  2179. W(" "); W(c); W(" =w loadw "); WL(a);
  2180. NewTemp(qr);
  2181. Op3("ceqw", qr, c, "0", FALSE);
  2182. NewTemp(c);
  2183. Op3("and", c, acc, qr, FALSE);
  2184. CopyOp(c, acc);
  2185. INC(i)
  2186. END;
  2187. NewLabel(lok); NewLabel(lbad);
  2188. Jnz(acc, lok, lbad);
  2189. EmitLabel(lbad);
  2190. Trap;
  2191. EmitLabel(lok)
  2192. END;
  2193. IF m > 0 THEN
  2194. IntStr(VAL(INTEGER, m * 4), n);
  2195. NewTemp(qr);
  2196. W(" "); W(qr); W(" =l call $memcpy(l ");
  2197. W(dst); W(", l "); W(src); W(", l "); W(n); WL(")")
  2198. END;
  2199. NewTemp(z);
  2200. Op3("xor", z, "0", "0", FALSE);
  2201. i := m;
  2202. WHILE i < dw DO
  2203. NewTemp(a);
  2204. IntStr(VAL(INTEGER, i * 4), off);
  2205. Op3L("add", a, dst, off);
  2206. Revive;
  2207. W(" storew "); W(z); W(", "); WL(a);
  2208. INC(i)
  2209. END
  2210. END CopySet;
  2211. PROCEDURE InSet (x: ARRAY OF CHAR; s: ARRAY OF CHAR; lo: INTEGER;
  2212. span: CARDINAL; VAR q: QVal);
  2213. (* Membership bit test with span trap; q is fresh w 0/1. *)
  2214. VAR off, offL, hiS, loS: QVal;
  2215. wi, bi, wil, off4, wa, wcur, m, a: QVal;
  2216. spanS: QVal;
  2217. BEGIN
  2218. NewTemp(off);
  2219. IntStr(lo, loS);
  2220. Op3("sub", off, x, loS, FALSE);
  2221. WidenIndex(off, offL);
  2222. IntStr(VAL(INTEGER, span) - 1, spanS);
  2223. CheckRange(offL, "0", spanS);
  2224. NewTemp(wi);
  2225. Op3("shr", wi, off, "5", FALSE);
  2226. NewTemp(bi);
  2227. Op3("and", bi, off, "31", FALSE);
  2228. WidenIndex(wi, wil);
  2229. NewTemp(off4);
  2230. Op3L("mul", off4, wil, "4");
  2231. NewTemp(wa);
  2232. Op3L("add", wa, s, off4);
  2233. NewTemp(wcur);
  2234. W(" "); W(wcur); W(" =w loadw "); WL(wa);
  2235. NewTemp(m);
  2236. Op3("shl", m, "1", bi, FALSE);
  2237. NewTemp(a);
  2238. Op3("and", a, wcur, m, FALSE);
  2239. NewTemp(q);
  2240. Op3("cnew", q, a, "0", FALSE)
  2241. END InSet;
  2242. PROCEDURE WidenIndex (idx: ARRAY OF CHAR; VAR q: QVal);
  2243. BEGIN
  2244. IF IsImm(idx) THEN Cpy(q, idx)
  2245. ELSE
  2246. NewTemp(q);
  2247. Revive;
  2248. W(" "); W(q); W(" =l extsw "); WL(idx)
  2249. END
  2250. END WidenIndex;
  2251. PROCEDURE OpenHi (base: ARRAY OF CHAR; VAR q: QVal);
  2252. VAR c: QVal;
  2253. BEGIN
  2254. NewTemp(c);
  2255. Revive;
  2256. W(" "); W(c); W(" =l loadl "); WL(base);
  2257. NewTemp(q);
  2258. Op3L("sub", q, c, "1")
  2259. END OpenHi;
  2260. PROCEDURE OpenHiChar (base: ARRAY OF CHAR; VAR q: QVal);
  2261. (* q := loadl(base) — the open-array count, i.e. the index of the NUL
  2262. terminator slot that CHAR arrays reserve. *)
  2263. BEGIN
  2264. NewTemp(q);
  2265. Revive;
  2266. W(" "); W(q); W(" =l loadl "); WL(base)
  2267. END OpenHiChar;
  2268. PROCEDURE LoadCount (base: ARRAY OF CHAR; VAR q: QVal);
  2269. BEGIN
  2270. NewTemp(q);
  2271. Revive;
  2272. W(" "); W(q); W(" =l loadl "); WL(base)
  2273. END LoadCount;
  2274. PROCEDURE CheckRange (idx, lo, hi: ARRAY OF CHAR);
  2275. (* l-domain operands (widened index, immediates, or open hi temp);
  2276. comparisons are long (result w). *)
  2277. VAR c1, c2, c: QVal;
  2278. lok, lbad: QVal;
  2279. BEGIN
  2280. NewTemp(c1);
  2281. Op3("csgel", c1, idx, lo, FALSE);
  2282. NewTemp(c2);
  2283. Op3("cslel", c2, idx, hi, FALSE);
  2284. NewTemp(c);
  2285. Op3("and", c, c1, c2, FALSE);
  2286. NewLabel(lok); NewLabel(lbad);
  2287. Jnz(c, lok, lbad);
  2288. EmitLabel(lbad);
  2289. Trap;
  2290. EmitLabel(lok)
  2291. END CheckRange;
  2292. PROCEDURE ElemAddr (base, idx, lo: ARRAY OF CHAR; elemT: INTEGER;
  2293. VAR q: QVal);
  2294. (* q := base + 8 + (idx - lo) * elemSize in l, fresh temp.
  2295. elemT is the array descriptor (open or fixed); all operands
  2296. l-domain (immediates pass, temps pre-widened). *)
  2297. VAR t1, t2, tb, sb: QVal;
  2298. BEGIN
  2299. NewTemp(t1);
  2300. Op3L("sub", t1, idx, lo);
  2301. IntStr(VAL(INTEGER, ElemSize(elemT)), sb);
  2302. NewTemp(t2);
  2303. Op3L("mul", t2, t1, sb);
  2304. NewTemp(tb);
  2305. Op3L("add", tb, base, "8");
  2306. NewTemp(q);
  2307. Op3L("add", q, tb, t2)
  2308. END ElemAddr;
  2309. PROCEDURE ElemLoad (addr: ARRAY OF CHAR; t: INTEGER; VAR q: QVal);
  2310. (* Loads one element of (element-)type t. Nested/pointer elements
  2311. are addresses (loadl). *)
  2312. VAR cls: INTEGER;
  2313. BEGIN
  2314. cls := SymTab.ClassOf(t);
  2315. NewTemp(q);
  2316. Revive;
  2317. W(" "); W(q);
  2318. IF cls = SymTab.ClReal THEN W(" =d loadd ")
  2319. ELSIF (cls = SymTab.ClArray) OR (cls = SymTab.ClPtr)
  2320. OR (cls = SymTab.ClLong) THEN
  2321. W(" =l loadl ")
  2322. ELSIF cls = SymTab.ClChar THEN W(" =w loadub ")
  2323. ELSE W(" =w loadw ")
  2324. END;
  2325. WL(addr)
  2326. END ElemLoad;
  2327. PROCEDURE ElemStore (addr, v: ARRAY OF CHAR; t: INTEGER);
  2328. VAR cls: INTEGER;
  2329. BEGIN
  2330. cls := SymTab.ClassOf(t);
  2331. Revive;
  2332. IF cls = SymTab.ClReal THEN W(" stored ")
  2333. ELSIF (cls = SymTab.ClArray) OR (cls = SymTab.ClPtr)
  2334. OR (cls = SymTab.ClLong) THEN
  2335. W(" storel ")
  2336. ELSIF cls = SymTab.ClChar THEN W(" storeb ")
  2337. ELSE W(" storew ")
  2338. END;
  2339. W(v); W(", "); WL(addr)
  2340. END ElemStore;
  2341. PROCEDURE CopyArray (dst: ARRAY OF CHAR; src: ARRAY OF CHAR;
  2342. t: INTEGER);
  2343. (* Whole-array copy with runtime count check + blit. Nested levels
  2344. recurse through a runtime loop; counts mismatch traps. dst/src
  2345. are descriptor address operands; t is the destination type. *)
  2346. VAR dc, sc, ok: QVal;
  2347. lok, lbad: QVal;
  2348. ecls: INTEGER;
  2349. n, da, sa: QVal;
  2350. sb: QVal;
  2351. qr: QVal;
  2352. i, c, de, se, di, si, dd0, doff, sd0, soff: QVal;
  2353. ltop, lbody, lend: QVal;
  2354. BEGIN
  2355. NewTemp(dc);
  2356. Revive;
  2357. W(" "); W(dc); W(" =l loadl "); WL(dst);
  2358. NewTemp(sc);
  2359. W(" "); W(sc); W(" =l loadl "); WL(src);
  2360. NewTemp(ok);
  2361. Op3("ceql", ok, dc, sc, FALSE);
  2362. NewLabel(lok); NewLabel(lbad);
  2363. Jnz(ok, lok, lbad);
  2364. EmitLabel(lbad);
  2365. Trap;
  2366. EmitLabel(lok);
  2367. ecls := ElemCls(t);
  2368. IF (ecls = SymTab.ClArray) THEN
  2369. NewTemp(i);
  2370. W(" "); W(i); W(" =l copy 0"); WL("");
  2371. NewLabel(ltop); NewLabel(lbody); NewLabel(lend);
  2372. EmitLabel(ltop);
  2373. NewTemp(c);
  2374. Op3("csltl", c, i, dc, FALSE);
  2375. Jnz(c, lbody, lend);
  2376. EmitLabel(lbody);
  2377. NewTemp(dd0); Op3L("add", dd0, dst, "8");
  2378. NewTemp(doff); Op3L("mul", doff, i, "8");
  2379. NewTemp(de); Op3L("add", de, dd0, doff);
  2380. NewTemp(sd0); Op3L("add", sd0, src, "8");
  2381. NewTemp(soff); Op3L("mul", soff, i, "8");
  2382. NewTemp(se); Op3L("add", se, sd0, soff);
  2383. NewTemp(di);
  2384. W(" "); W(di); W(" =l loadl "); WL(de);
  2385. NewTemp(si);
  2386. W(" "); W(si); W(" =l loadl "); WL(se);
  2387. CopyArray(di, si, SymTab.ArrayElem(t));
  2388. Op3L("add", i, i, "1");
  2389. Jmp(ltop);
  2390. EmitLabel(lend)
  2391. ELSE
  2392. IntStr(VAL(INTEGER, ElemSize(t)), sb);
  2393. NewTemp(n);
  2394. Op3L("mul", n, dc, sb);
  2395. NewTemp(da);
  2396. Op3L("add", da, dst, "8");
  2397. NewTemp(sa);
  2398. Op3L("add", sa, src, "8");
  2399. NewTemp(qr);
  2400. W(" "); W(qr); W(" =l call $memcpy(l ");
  2401. W(da); W(", l "); W(sa); W(", l "); W(n); WL(")")
  2402. END
  2403. END CopyArray;
  2404. PROCEDURE DeclStr (text: ARRAY OF CHAR; VAR q: QVal);
  2405. (* Records a quoted literal for top-level emission at EndModule and
  2406. returns its address operand ($strN). Data definitions may only
  2407. appear outside functions, but literals occur mid-body. *)
  2408. VAR nm: QVal;
  2409. BEGIN
  2410. Cpy(nm, "str");
  2411. AppNum(nm, nStr);
  2412. IF nStr <= HIGH(strNams) THEN
  2413. Cpy(strNams[nStr], nm);
  2414. Cpy(strTexts[nStr], text);
  2415. INC(nStr)
  2416. END;
  2417. Cpy(q, "$");
  2418. App(q, nm)
  2419. END DeclStr;
  2420. PROCEDURE DeclUStr (text: ARRAY OF CHAR; VAR q: QVal;
  2421. VAR single: BOOLEAN; VAR cp: INTEGER;
  2422. VAR ok: BOOLEAN);
  2423. (* text is the raw lexeme U'...' / U"..." Strict RFC3629-decode the
  2424. bytes between the quotes. Exactly one codepoint -> single:=TRUE,
  2425. cp:=it. Otherwise record a UString descriptor and q := "$ustrN".
  2426. ok:=FALSE for invalid UTF-8 (overlong / surrogate / >10FFFF /
  2427. truncation). *)
  2428. VAR L, i, cnt, base: CARDINAL;
  2429. b0, b1, b2, b3, ch: INTEGER;
  2430. nm: QVal;
  2431. PROCEDURE Cont (VAR b: INTEGER): BOOLEAN;
  2432. (* consume a continuation byte, or fail *)
  2433. BEGIN
  2434. IF i >= L - 1 THEN RETURN FALSE END;
  2435. b := ORD(text[i]); INC(i);
  2436. RETURN (b >= 128) AND (b <= 191)
  2437. END Cont;
  2438. BEGIN
  2439. single := FALSE; cp := 0; ok := TRUE;
  2440. q[0] := "0"; q[1] := CHR(0);
  2441. L := Len(text);
  2442. IF (L < 3) OR (text[0] # "U") THEN ok := FALSE; RETURN END;
  2443. i := 2; cnt := 0; base := ustrUsed;
  2444. WHILE i < L - 1 DO
  2445. b0 := ORD(text[i]); INC(i);
  2446. ch := -1;
  2447. IF b0 <= 127 THEN
  2448. ch := b0
  2449. ELSIF (b0 >= 194) AND (b0 <= 223) THEN
  2450. IF Cont(b1) THEN ch := ((b0 - 192) * 64) + (b1 - 128) END
  2451. ELSIF (b0 >= 224) AND (b0 <= 239) THEN
  2452. IF Cont(b1) AND Cont(b2) THEN
  2453. ch := ((b0 - 224) * 4096) + ((b1 - 128) * 64) + (b2 - 128);
  2454. IF ch < 2048 THEN ch := -1 END
  2455. END
  2456. ELSIF (b0 >= 240) AND (b0 <= 244) THEN
  2457. IF Cont(b1) AND Cont(b2) AND Cont(b3) THEN
  2458. ch := ((b0 - 240) * 262144) + ((b1 - 128) * 4096)
  2459. + ((b2 - 128) * 64) + (b3 - 128);
  2460. IF ch < 65536 THEN ch := -1 END
  2461. END
  2462. ELSE ok := FALSE
  2463. END;
  2464. IF ok AND (ch < 0) THEN ok := FALSE END;
  2465. IF ok AND (ch >= 55296) AND (ch <= 57343) THEN ok := FALSE END;
  2466. IF ok AND (ch > 1114111) THEN ok := FALSE END;
  2467. IF NOT ok THEN RETURN END;
  2468. IF ustrUsed <= HIGH(ustrPool) THEN
  2469. ustrPool[ustrUsed] := ch; INC(ustrUsed)
  2470. END;
  2471. INC(cnt)
  2472. END;
  2473. IF cnt = 1 THEN
  2474. single := TRUE; cp := ustrPool[base]
  2475. ELSE
  2476. Cpy(nm, "ustr"); AppNum(nm, ustrN);
  2477. IF ustrN <= HIGH(ustrNams) THEN
  2478. Cpy(ustrNams[ustrN], nm);
  2479. ustrStart[ustrN] := base; ustrCount[ustrN] := cnt; INC(ustrN)
  2480. END;
  2481. Cpy(q, "$"); App(q, nm)
  2482. END
  2483. END DeclUStr;
  2484. PROCEDURE FlushUStrings;
  2485. VAR k, i: CARDINAL;
  2486. bv: QVal;
  2487. BEGIN
  2488. IF NOT opened THEN RETURN END;
  2489. k := 0;
  2490. WHILE k < ustrN DO
  2491. W("data $"); W(ustrNams[k]); W(" = { l ");
  2492. IntStr(VAL(INTEGER, ustrCount[k]), bv); W(bv);
  2493. i := 0;
  2494. WHILE i < ustrCount[k] DO
  2495. W(", w ");
  2496. IntStr(ustrPool[ustrStart[k] + i], bv); W(bv);
  2497. INC(i)
  2498. END;
  2499. WL(" }");
  2500. INC(k)
  2501. END
  2502. END FlushUStrings;
  2503. PROCEDURE DeclCharStr (ch: ARRAY OF CHAR; VAR q: QVal);
  2504. VAR v: INTEGER;
  2505. txt: ARRAY [0 .. 3] OF CHAR;
  2506. BEGIN
  2507. IF ParseInt(ch, v) AND (v >= 0) AND (v < 256) THEN
  2508. txt[0] := '"'; txt[1] := CHR(v); txt[2] := '"'; txt[3] := CHR(0);
  2509. DeclStr(txt, q)
  2510. ELSE Cpy(q, "0")
  2511. END
  2512. END DeclCharStr;
  2513. PROCEDURE FlushStrings;
  2514. (* Emits all recorded string literals as top-level data. *)
  2515. VAR k, i, L: CARDINAL;
  2516. bv: QVal;
  2517. BEGIN
  2518. IF NOT opened THEN RETURN END;
  2519. k := 0;
  2520. WHILE k < nStr DO
  2521. L := Len(strTexts[k]);
  2522. IF L >= 2 THEN
  2523. W("data $"); W(strNams[k]);
  2524. W(" = { l ");
  2525. IntStr(VAL(INTEGER, L - 2), bv);
  2526. W(bv);
  2527. i := 1;
  2528. WHILE i < L - 1 DO
  2529. W(", b ");
  2530. IntStr(ORD(strTexts[k][i]), bv);
  2531. W(bv);
  2532. INC(i)
  2533. END;
  2534. W(", b 0"); (* NUL terminator (count excludes it) *)
  2535. WL(" }")
  2536. END;
  2537. INC(k)
  2538. END
  2539. END FlushStrings;
  2540. PROCEDURE DeclArr (name: ARRAY OF CHAR; t: INTEGER);
  2541. BEGIN
  2542. IF SymTab.IsOpenArray(t) OR (SymTab.ArrayDepth(t) = 0) THEN
  2543. DataLine(name, FALSE, "0"); RETURN
  2544. END;
  2545. ArrData(name, t)
  2546. END DeclArr;
  2547. PROCEDURE PushLoop (exit: ARRAY OF CHAR);
  2548. BEGIN
  2549. IF loopTop <= HIGH(loopSt) THEN
  2550. Cpy(loopSt[loopTop], exit); INC(loopTop)
  2551. END
  2552. END PushLoop;
  2553. PROCEDURE PopLoop;
  2554. BEGIN
  2555. IF loopTop > 0 THEN DEC(loopTop) END
  2556. END PopLoop;
  2557. PROCEDURE TopLoop (VAR exit: QVal): BOOLEAN;
  2558. BEGIN
  2559. IF loopTop = 0 THEN RETURN FALSE END;
  2560. Cpy(exit, loopSt[loopTop - 1]);
  2561. RETURN TRUE
  2562. END TopLoop;
  2563. PROCEDURE CmpL (op: INTEGER; l, r: ARRAY OF CHAR; VAR q: QVal);
  2564. VAR mn : ARRAY [0 .. 7] OF CHAR;
  2565. BEGIN
  2566. mn[0] := CHR(0);
  2567. IF op = SymTab.OpEq THEN Cpy(mn, "ceql")
  2568. ELSE Cpy(mn, "cnel")
  2569. END;
  2570. NewTemp(q);
  2571. Op3(mn, q, l, r, FALSE)
  2572. END CmpL;
  2573. PROCEDURE Remark (s: ARRAY OF CHAR);
  2574. BEGIN
  2575. Revive;
  2576. W("# "); WL(s)
  2577. END Remark;
  2578. (* ---------------- heap (step 3.6: extern malloc/free) ---------------- *)
  2579. (* Only the object skeleton is initialized (array counts); elements
  2580. and members stay garbage per Wirth, except nested objects which
  2581. are allocated recursively. DISPOSE is shallow (documented). *)
  2582. PROCEDURE HeapSize (t: INTEGER): CARDINAL;
  2583. VAR cls: INTEGER;
  2584. n: CARDINAL;
  2585. BEGIN
  2586. cls := SymTab.ClassOf(t);
  2587. IF cls = SymTab.ClArray THEN
  2588. n := SymTab.ArrayLen(t) * ElemSize(t);
  2589. IF SymTab.IsCharArray(t) THEN n := n + 1 END;
  2590. IF SymTab.IsUCharArray(t) THEN n := n + 4 END;
  2591. RETURN 8 + n
  2592. END;
  2593. RETURN SymTab.TypeSize(t)
  2594. END HeapSize;
  2595. PROCEDURE NewHeap (t: SymTab.TypeIndex; VAR q: QVal);
  2596. VAR nb: QVal;
  2597. BEGIN
  2598. NewTemp(q);
  2599. Revive;
  2600. IntStr(VAL(INTEGER, HeapSize(t)), nb);
  2601. W(" "); W(q);
  2602. IF useStack THEN W(" =l alloc8 "); W(nb); WL("")
  2603. ELSE W(" =l call $malloc(l "); W(nb); WL(")")
  2604. END
  2605. END NewHeap;
  2606. PROCEDURE FreeHeap (v: ARRAY OF CHAR);
  2607. BEGIN
  2608. Revive;
  2609. W(" call $free(l "); W(v); WL(")")
  2610. END FreeHeap;
  2611. PROCEDURE InitHeap (addr: ARRAY OF CHAR; t: SymTab.TypeIndex);
  2612. VAR cls, ecls: INTEGER;
  2613. n, i: CARDINAL;
  2614. fn: SymTab.Name;
  2615. ft: SymTab.TypeIndex;
  2616. nb, ea, eb, fa, fb: QVal;
  2617. BEGIN
  2618. cls := SymTab.ClassOf(t);
  2619. IF (cls = SymTab.ClClass) AND SymTab.HasVTable(t) THEN
  2620. (* install the class's vtable pointer *)
  2621. VtRef(t, eb);
  2622. IntStr(SymTab.VptrOffset(t), nb);
  2623. NewTemp(ea);
  2624. Op3L("add", ea, addr, nb);
  2625. Revive;
  2626. W(" storel "); W(eb); W(", "); WL(ea)
  2627. END;
  2628. IF cls = SymTab.ClArray THEN
  2629. IntStr(VAL(INTEGER, SymTab.ArrayLen(t)), nb);
  2630. Revive;
  2631. W(" storel "); W(nb); W(", "); WL(addr);
  2632. IF SymTab.IsCharArray(t) THEN
  2633. (* NUL terminator slot at index = count *)
  2634. IntStr(VAL(INTEGER, 8 + SymTab.ArrayLen(t)), nb);
  2635. NewTemp(ea);
  2636. Op3L("add", ea, addr, nb);
  2637. Revive;
  2638. W(" storeb 0, "); WL(ea)
  2639. END;
  2640. IF SymTab.IsUCharArray(t) THEN
  2641. (* 0-codepoint terminator at index = count (4-byte element) *)
  2642. IntStr(VAL(INTEGER, 8 + SymTab.ArrayLen(t) * 4), nb);
  2643. NewTemp(ea);
  2644. Op3L("add", ea, addr, nb);
  2645. Revive;
  2646. W(" storew 0, "); WL(ea)
  2647. END;
  2648. ecls := SymTab.ClassOf(SymTab.ArrayElem(t));
  2649. IF ecls = SymTab.ClArray THEN
  2650. n := SymTab.ArrayLen(t);
  2651. i := 0;
  2652. WHILE i < n DO
  2653. IntStr(VAL(INTEGER, 8 + i * 8), nb);
  2654. NewTemp(ea);
  2655. Op3L("add", ea, addr, nb);
  2656. NewHeap(SymTab.ArrayElem(t), eb);
  2657. InitHeap(eb, SymTab.ArrayElem(t)); (* init the sub-array *)
  2658. Revive;
  2659. W(" storel "); W(eb); W(", "); WL(ea);
  2660. INC(i)
  2661. END
  2662. END
  2663. ELSIF (cls = SymTab.ClRecord) OR (cls = SymTab.ClClass) THEN
  2664. n := SymTab.FieldCount(t);
  2665. i := 0;
  2666. WHILE i < n DO
  2667. SymTab.FieldName(t, i, fn);
  2668. ft := SymTab.FieldType(t, fn);
  2669. ecls := SymTab.ClassOf(ft);
  2670. IF ecls = SymTab.ClArray THEN
  2671. (* inline array field: initialize its header in place *)
  2672. IntStr(SymTab.FieldOffset(t, fn), nb);
  2673. NewTemp(fa);
  2674. Op3L("add", fa, addr, nb);
  2675. InitHeap(fa, ft)
  2676. ELSIF (ecls = SymTab.ClRecord) OR (ecls = SymTab.ClClass) THEN
  2677. IntStr(SymTab.FieldOffset(t, fn), nb);
  2678. NewTemp(fa);
  2679. Op3L("add", fa, addr, nb);
  2680. InitHeap(fa, ft)
  2681. END;
  2682. INC(i)
  2683. END
  2684. END
  2685. END InitHeap;
  2686. BEGIN
  2687. opened := FALSE;
  2688. inBody := FALSE;
  2689. dead := FALSE;
  2690. nTemp := 0; nLab := 0; loopTop := 0; nR := 0; nStr := 0;
  2691. withTop := 0;
  2692. noEmit := FALSE; inFunc := FALSE; useStack := FALSE;
  2693. nLoc := 0; nPar := 0; nArg := 0; nn := 0; callDepth := 0;
  2694. recvArmed := FALSE;
  2695. funcDepth := 0; scopeTop := 0; scopeBase[0] := 0;
  2696. outSel := 0
  2697. END QbeGen.