QbeGen.mod 75 KB

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