| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680 |
- IMPLEMENTATION MODULE Lower;
- (* Scalar/boolean/control-flow walk (L0-L2): see Lower.def and
- docs/plan-lower.md. The mutually recursive walkers are declared in
- Lower.def (gm2's link pass rejects FORWARD, V3 has no whole-module
- pass, so a definition-visible declaration breaks the cycle). *)
- IMPORT AST, SymTab, QbeGen;
- (* ---------------- self-contained symbol tables (zero SymTab surface) ----
- Flat arrays only, no ARRAY OF RECORD: a record's array field loses its
- length word in static array data, and V3 miscompiles that when the
- field is passed as an open array (docs/wip/nested-array-field-bug.mod).
- Names live in flat CHAR arrays indexed manually at slot * 64. *)
- CONST
- MaxSym = 8192; (* whole-session: >4096 vars/consts/params *)
- MaxName = 63;
- MaxProc = 2048; (* whole-session: the compiler declares >1200 procs *)
- MaxImp = 512; (* per-unit imports *)
- MaxPar = 32;
- HashSize = 16384; (* power of two > MaxSym, MaxProc: open addressing *)
- MaxCFold = 256; (* named compile-time set constants per session *)
- MaxCFoldW = 16; (* mask words (512 bits); wider sets are a 230 *)
- VAR
- symName : ARRAY [0 .. MaxSym * 64 - 1] OF CHAR;
- symKind : ARRAY [0 .. MaxSym - 1] OF INTEGER;
- symTy : ARRAY [0 .. MaxSym - 1] OF SymTab.TypeIndex;
- symVal : ARRAY [0 .. MaxSym * 64 - 1] OF CHAR; (* const value, flat *)
- nSyms : CARDINAL;
- procName : ARRAY [0 .. MaxProc * 64 - 1] OF CHAR;
- procUid : ARRAY [0 .. MaxProc - 1] OF CARDINAL;
- procRes : ARRAY [0 .. MaxProc - 1] OF SymTab.TypeIndex;
- procDep : ARRAY [0 .. MaxProc - 1] OF CARDINAL;
- procExt : ARRAY [0 .. MaxProc - 1] OF BOOLEAN;
- procVa : ARRAY [0 .. MaxProc - 1] OF BOOLEAN; (* C varargs *)
- procNpar : ARRAY [0 .. MaxProc - 1] OF CARDINAL;
- parTy : ARRAY [0 .. MaxProc * MaxPar - 1] OF SymTab.TypeIndex;
- parVar : ARRAY [0 .. MaxProc * MaxPar - 1] OF BOOLEAN;
- parName : ARRAY [0 .. MaxProc * MaxPar * 64 - 1] OF CHAR;
- nProcs : CARDINAL;
- (* Open-addressing indexes into symName/procName: entry = index+1,
- 0 = empty. A whole-session linear scan is too slow (thousands of
- lookups over thousands of entries), so FindSym/FindProc hash. *)
- symHash : ARRAY [0 .. HashSize - 1] OF INTEGER;
- procHash : ARRAY [0 .. HashSize - 1] OF INTEGER;
- (* Per-unit imports (FROM M IMPORT P): the exact proc entry for a
- name, so an unqualified call resolves to the imported proc even
- when another module declares the same name. Reset each unit. *)
- impProc : ARRAY [0 .. MaxImp - 1] OF INTEGER;
- impName : ARRAY [0 .. MaxImp * 64 - 1] OF CHAR;
- nImps : CARDINAL;
- (* Record-array globals too large to expand inline: their template is
- emitted with their data and their elements are initialised by a
- run-time loop (QbeGen.InitRecArray) in the unit's init. Per unit. *)
- nCountInit : CARDINAL;
- countArr : ARRAY [0 .. 63] OF SymTab.Name;
- countTyp : ARRAY [0 .. 63] OF SymTab.TypeIndex;
- (* GOTO/labels: user label name -> QbeGen label, per procedure. *)
- nUserLab : CARDINAL;
- userLabNm : ARRAY [0 .. 255 * 64 - 1] OF CHAR;
- userLabQ : ARRAY [0 .. 255] OF QbeGen.QVal;
- (* Scoping: each symbol records the procedure that owns it (-1 =
- module level); FindSym searches the emitted procedure's chain
- innermost-first so common names (s, i, k) do not collide across
- procedures. *)
- symOwner : ARRAY [0 .. MaxSym - 1] OF INTEGER;
- procParent : ARRAY [0 .. MaxProc - 1] OF INTEGER;
- procSymBase : ARRAY [0 .. MaxProc - 1] OF CARDINAL;
- procSymTop : ARRAY [0 .. MaxProc - 1] OF CARDINAL;
- scopeStack : ARRAY [0 .. MaxProc - 1] OF INTEGER;
- scopeTop : CARDINAL;
- curScopeProc : INTEGER; (* owner while parsing: enclosing proc *)
- emitProc : INTEGER; (* owner while emitting: current proc *)
- qualUid : CARDINAL; (* uid of the last M.P alias resolved *)
- qualIsVar : BOOLEAN; (* last M.P alias is a proc-typed variable *)
- withRec : ARRAY [0 .. 15] OF SymTab.TypeIndex;
- withTop : CARDINAL;
- curMethCls : SymTab.TypeIndex; (* class bound by the last obj.Method *)
- curImplCls : SymTab.TypeIndex; (* class whose implementation is open *)
- curUnitName : SymTab.Name; (* unit being re-emitted (LowerSession) *)
- (* Compile-time set constants (SET CONSTs): masks the grammar computes
- and Lower re-emits as pooled static data. Name-keyed, latest wins,
- exactly like symVal. *)
- cfoldN : CARDINAL;
- cfoldName : ARRAY [0 .. MaxCFold * 64 - 1] OF CHAR;
- cfoldW : ARRAY [0 .. MaxCFold - 1] OF CARDINAL;
- cfoldLo : ARRAY [0 .. MaxCFold - 1] OF INTEGER;
- cfoldMask : ARRAY [0 .. MaxCFold * MaxCFoldW - 1] OF CARDINAL;
- PROCEDURE CopyN (src: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
- VAR i: CARDINAL;
- BEGIN
- i := 0;
- WHILE (i < MaxName) AND (src[i] # CHR(0)) DO dst[i] := src[i]; INC(i) END;
- dst[i] := CHR(0)
- END CopyN;
- PROCEDURE CopySpan (VAR src: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR;
- srcOff, dstOff: CARDINAL);
- (* src is a VAR open array on purpose: gm2 copies a value open-array
- formal to a stack VLA of its full length, which overflows the stack
- for the multi-megabyte tables below. *)
- VAR i: CARDINAL;
- BEGIN
- i := 0;
- WHILE (i < MaxName) AND (src[srcOff + i] # CHR(0)) DO
- dst[dstOff + i] := src[srcOff + i]; INC(i)
- END;
- dst[dstOff + i] := CHR(0)
- END CopySpan;
- PROCEDURE HashStr (name: ARRAY OF CHAR): CARDINAL;
- VAR h: CARDINAL; i: CARDINAL;
- BEGIN
- h := 5381; i := 0;
- WHILE (i <= HIGH(name)) AND (name[i] # CHR(0)) DO
- h := (h * 33 + VAL(CARDINAL, ORD(name[i]))) MOD HashSize; INC(i)
- END;
- RETURN h
- END HashStr;
- PROCEDURE SpanEq (VAR arr: ARRAY OF CHAR; off: CARDINAL;
- name: ARRAY OF CHAR): BOOLEAN;
- VAR j: CARDINAL;
- BEGIN
- j := 0;
- WHILE (j < MaxName) AND (name[j] # CHR(0)) DO
- IF arr[off + j] # name[j] THEN RETURN FALSE END;
- INC(j)
- END;
- RETURN arr[off + j] = name[j]
- END SpanEq;
- PROCEDURE HashPut (VAR htab: ARRAY OF INTEGER; VAR names: ARRAY OF CHAR;
- name: ARRAY OF CHAR; idx: CARDINAL);
- (* Insert idx, replacing an existing entry with the same name so the
- latest declaration wins (the linear scan used to search backward). *)
- VAR h: CARDINAL;
- BEGIN
- h := HashStr(name);
- LOOP
- IF htab[h] = 0 THEN htab[h] := VAL(INTEGER, idx) + 1; RETURN END;
- IF SpanEq(names, VAL(CARDINAL, htab[h] - 1) * 64, name) THEN
- htab[h] := VAL(INTEGER, idx) + 1; RETURN
- END;
- h := (h + 1) MOD HashSize
- END
- END HashPut;
- PROCEDURE HashGet (VAR htab: ARRAY OF INTEGER; VAR names: ARRAY OF CHAR;
- name: ARRAY OF CHAR): INTEGER;
- VAR h: CARDINAL;
- BEGIN
- h := HashStr(name);
- LOOP
- IF htab[h] = 0 THEN RETURN -1 END;
- IF SpanEq(names, VAL(CARDINAL, htab[h] - 1) * 64, name) THEN
- RETURN htab[h] - 1
- END;
- h := (h + 1) MOD HashSize
- END
- END HashGet;
- PROCEDURE Reset;
- VAR i: CARDINAL;
- BEGIN
- nSyms := 0; nProcs := 0;
- scopeTop := 0; curScopeProc := -1; emitProc := -1;
- i := 0;
- WHILE i < HashSize DO symHash[i] := 0; procHash[i] := 0; INC(i) END
- END Reset;
- PROCEDURE NoteVar (name: ARRAY OF CHAR; kind: INTEGER; t: SymTab.TypeIndex);
- BEGIN
- IF nSyms >= MaxSym THEN RETURN END;
- CopySpan(name, symName, 0, nSyms * 64);
- symKind[nSyms] := kind; symTy[nSyms] := t;
- symOwner[nSyms] := curScopeProc;
- HashPut(symHash, symName, name, nSyms);
- INC(nSyms)
- END NoteVar;
- PROCEDURE NoteConstVal (val: ARRAY OF CHAR);
- (* Records the evaluated value of the CONST just entered by NoteVar.
- Const values are per-unit and cross-module names collide (every
- module has EOL), so they live here, not in SymTab. *)
- BEGIN
- IF nSyms > 0 THEN CopySpan(val, symVal, 0, (nSyms - 1) * 64) END
- END NoteConstVal;
- PROCEDURE ScopeEnter;
- (* Called at a procedure heading, before NoteProc. Pushes the enclosing
- procedure as the parent scope. *)
- BEGIN
- IF scopeTop <= HIGH(scopeStack) THEN
- scopeStack[scopeTop] := curScopeProc;
- INC(scopeTop)
- END
- END ScopeEnter;
- PROCEDURE ScopeLeave;
- BEGIN
- IF curScopeProc >= 0 THEN procSymTop[curScopeProc] := nSyms END;
- IF scopeTop > 0 THEN
- DEC(scopeTop);
- curScopeProc := scopeStack[scopeTop]
- END
- END ScopeLeave;
- PROCEDURE ReenterScopeProc (ty: INTEGER);
- (* Restores a previously noted proc entry as current for the re-parse
- pass (its registration ran once in the skip pass): pushes the
- current owner and resumes ty, so the Block-end ScopeLeave pops back
- and nested NoteProc/NoteVar attach under the right parent. *)
- BEGIN
- IF (ty < 0) OR (ty >= VAL(INTEGER, nProcs)) THEN RETURN END;
- IF scopeTop <= HIGH(scopeStack) THEN
- scopeStack[scopeTop] := curScopeProc;
- INC(scopeTop)
- END;
- curScopeProc := ty
- END ReenterScopeProc;
- PROCEDURE CurScopeProc (): INTEGER;
- (* The proc entry NoteProc most recently made current (ty stamped on the
- NkProcDecl); captured by the parser's span table for the re-parse. *)
- BEGIN
- RETURN curScopeProc
- END CurScopeProc;
- PROCEDURE NoteProc (name: ARRAY OF CHAR; uid: CARDINAL;
- res: SymTab.TypeIndex; depth: CARDINAL; ext: BOOLEAN);
- BEGIN
- IF nProcs >= MaxProc THEN RETURN END;
- CopySpan(name, procName, 0, nProcs * 64);
- procUid[nProcs] := uid; procRes[nProcs] := res;
- procDep[nProcs] := depth; procExt[nProcs] := ext;
- procNpar[nProcs] := 0;
- procParent[nProcs] := curScopeProc;
- procSymBase[nProcs] := nSyms;
- curScopeProc := VAL(INTEGER, nProcs);
- HashPut(procHash, procName, name, nProcs);
- INC(nProcs)
- END NoteProc;
- PROCEDURE AliasProc (name: ARRAY OF CHAR; uid: CARDINAL;
- res: SymTab.TypeIndex; depth: CARDINAL; ext: BOOLEAN);
- (* A generic procedure formal bound to an actual procedure: add a
- proc-table entry so calls through the formal resolve to the actual,
- without disturbing the scope stack (unlike NoteProc). *)
- BEGIN
- IF nProcs >= MaxProc THEN RETURN END;
- CopySpan(name, procName, 0, nProcs * 64);
- procUid[nProcs] := uid; procRes[nProcs] := res;
- procDep[nProcs] := depth; procExt[nProcs] := ext;
- procNpar[nProcs] := 0;
- procParent[nProcs] := -1;
- procSymBase[nProcs] := nSyms;
- HashPut(procHash, procName, name, nProcs);
- INC(nProcs)
- END AliasProc;
- PROCEDURE MarkProcExternal;
- (* The grammar calls this from the EXTERNAL branch, after NoteProc, so
- the walk records that the callee takes no static link. *)
- BEGIN
- IF nProcs > 0 THEN procExt[nProcs - 1] := TRUE END
- END MarkProcExternal;
- PROCEDURE NoteCProc (name: ARRAY OF CHAR);
- (* FROM libc IMPORT P: a permissive C varargs external. NoteProc sets
- curScopeProc; restore it so the next import is not seen as nested. *)
- VAR save: INTEGER;
- BEGIN
- save := curScopeProc;
- curScopeProc := -1;
- NoteProc(name, 0, SymTab.InvalidType, 0, TRUE);
- curScopeProc := save;
- IF nProcs > 0 THEN
- procExt[nProcs - 1] := TRUE;
- procVa[nProcs - 1] := TRUE;
- procNpar[nProcs - 1] := 0;
- procParent[nProcs - 1] := -1
- END
- END NoteCProc;
- PROCEDURE NoteProcNode (n: AST.Node);
- (* Records the proc-table index in the NkProcDecl's ty field, so
- LowerDecls can find the exact entry. FindProc(name) would return the
- last duplicate (e.g. the two nested `Sep`s), giving the wrong uid and
- the wrong scope for its locals. *)
- BEGIN
- IF nProcs > 0 THEN AST.SetTy(n, VAL(INTEGER, nProcs - 1)) END
- END NoteProcNode;
- PROCEDURE ProcInfoIdx (pi: INTEGER; VAR uid: CARDINAL;
- VAR res: SymTab.TypeIndex; VAR depth: CARDINAL;
- VAR ext: BOOLEAN; VAR npar: CARDINAL): BOOLEAN;
- BEGIN
- IF (pi < 0) OR (pi >= VAL(INTEGER, nProcs)) THEN RETURN FALSE END;
- uid := procUid[pi]; res := procRes[pi]; depth := procDep[pi];
- ext := procExt[pi]; npar := procNpar[pi];
- IF npar > MaxPar THEN npar := MaxPar END;
- RETURN TRUE
- END ProcInfoIdx;
- PROCEDURE ParamInfoIdx (pi: INTEGER; i: CARDINAL;
- VAR t: SymTab.TypeIndex; VAR isVar: BOOLEAN;
- VAR pn: ARRAY OF CHAR): BOOLEAN;
- BEGIN
- IF (pi < 0) OR (pi >= MaxProc)
- OR (i >= procNpar[pi]) OR (procNpar[pi] > MaxPar) THEN
- RETURN FALSE
- END;
- t := parTy[VAL(CARDINAL, pi) * MaxPar + i];
- isVar := parVar[VAL(CARDINAL, pi) * MaxPar + i];
- CopySpan(parName, pn, VAL(CARDINAL, pi) * MaxPar * 64 + i * 64, 0);
- RETURN TRUE
- END ParamInfoIdx;
- PROCEDURE SetProcRes (t: SymTab.TypeIndex);
- BEGIN
- IF nProcs > 0 THEN procRes[nProcs - 1] := t END
- END SetProcRes;
- PROCEDURE NoteParam (name: ARRAY OF CHAR; t: SymTab.TypeIndex; isVar: BOOLEAN);
- VAR j: CARDINAL;
- BEGIN
- IF nProcs = 0 THEN RETURN END;
- j := procNpar[nProcs - 1];
- IF j >= MaxPar THEN RETURN END;
- CopySpan(name, parName, 0, (nProcs - 1) * MaxPar * 64 + j * 64);
- parTy[(nProcs - 1) * MaxPar + j] := t;
- parVar[(nProcs - 1) * MaxPar + j] := isVar;
- procNpar[nProcs - 1] := j + 1
- END NoteParam;
- PROCEDURE NameEq (i: CARDINAL; name: ARRAY OF CHAR): BOOLEAN;
- VAR j: CARDINAL;
- BEGIN
- j := 0;
- WHILE (j < MaxName) AND (name[j] # CHR(0)) DO
- IF symName[i * 64 + j] # name[j] THEN RETURN FALSE END;
- INC(j)
- END;
- RETURN symName[i * 64 + j] = name[j]
- END NameEq;
- PROCEDURE FindSym (name: ARRAY OF CHAR; VAR kind: INTEGER;
- VAR t: SymTab.TypeIndex): BOOLEAN;
- (* Locals/params of the emitted procedure (and its enclosing procedures)
- shadow module-level symbols, so walk the scope chain innermost-first
- and only then fall back to the hash for a module-level symbol. *)
- VAR i: INTEGER; owner: INTEGER;
- BEGIN
- owner := emitProc;
- WHILE owner >= 0 DO
- (* procSymTop is unset for headings that never ScopeLeave (class
- method declarations); fall back to the whole table then. *)
- IF procSymTop[owner] > 0 THEN
- i := VAL(INTEGER, procSymTop[owner]) - 1
- ELSE i := VAL(INTEGER, nSyms) - 1
- END;
- WHILE i >= VAL(INTEGER, procSymBase[owner]) DO
- IF (symOwner[i] = owner) AND NameEq(VAL(CARDINAL, i), name) THEN
- kind := symKind[i]; t := symTy[i]; RETURN TRUE
- END;
- DEC(i)
- END;
- owner := procParent[owner]
- END;
- i := HashGet(symHash, symName, name);
- IF (i >= 0) AND (symOwner[i] = -1) THEN
- kind := symKind[i]; t := symTy[i]; RETURN TRUE
- END;
- RETURN FALSE
- END FindSym;
- PROCEDURE GetConstVal (name: ARRAY OF CHAR; VAR v: ARRAY OF CHAR): BOOLEAN;
- VAR i: INTEGER;
- BEGIN
- i := HashGet(symHash, symName, name);
- IF (i >= 0) AND (symKind[i] = SymTab.KindConst) THEN
- CopySpan(symVal, v, VAL(CARDINAL, i) * 64, 0);
- IF v[0] # CHR(0) THEN RETURN TRUE END
- END;
- (* a literal propagated with its enum type lives only in SymTab *)
- v[0] := CHR(0);
- RETURN SymTab.GetSymVal(name, v)
- END GetConstVal;
- PROCEDURE SetConstVal (name: ARRAY OF CHAR; val: ARRAY OF CHAR);
- (* Refresh a CONST's recorded value after Lower re-emits its aggregate
- constructor (the grammar's value was suppressed under NoEmit). *)
- VAR i: INTEGER;
- BEGIN
- i := HashGet(symHash, symName, name);
- IF i >= 0 THEN CopySpan(val, symVal, 0, VAL(CARDINAL, i) * 64) END
- END SetConstVal;
- PROCEDURE ProcNameEq (i: CARDINAL; name: ARRAY OF CHAR): BOOLEAN;
- VAR j: CARDINAL;
- BEGIN
- j := 0;
- WHILE (j < MaxName) AND (name[j] # CHR(0)) DO
- IF procName[i * 64 + j] # name[j] THEN RETURN FALSE END;
- INC(j)
- END;
- RETURN procName[i * 64 + j] = name[j]
- END ProcNameEq;
- PROCEDURE ProcVisible (i: INTEGER): BOOLEAN;
- (* TRUE when proc i is nested directly in the emitted procedure or one
- of its enclosing procedures (or is module-level). *)
- VAR owner: INTEGER;
- BEGIN
- owner := emitProc;
- WHILE owner >= 0 DO
- IF procParent[i] = owner THEN RETURN TRUE END;
- owner := procParent[owner]
- END;
- RETURN procParent[i] = -1
- END ProcVisible;
- PROCEDURE ImpNameEq (i: CARDINAL; name: ARRAY OF CHAR): BOOLEAN;
- BEGIN
- RETURN SpanEq(impName, i * 64, name)
- END ImpNameEq;
- PROCEDURE FindProc (name: ARRAY OF CHAR): INTEGER;
- (* Scope-aware: a nested procedure (e.g. one of two `Sep`s) must resolve
- to the one in the emitted procedure's chain, not the last duplicate.
- The current unit's imports win over any same-named proc. *)
- VAR i: INTEGER; owner: INTEGER;
- BEGIN
- i := 0;
- WHILE i < VAL(INTEGER, nImps) DO
- IF ImpNameEq(VAL(CARDINAL, i), name) THEN RETURN impProc[i] END;
- INC(i)
- END;
- i := HashGet(procHash, procName, name);
- IF (i >= 0) AND ProcVisible(i) THEN RETURN i END;
- owner := emitProc;
- WHILE owner >= 0 DO
- i := VAL(INTEGER, nProcs) - 1;
- WHILE i > 0 DO
- DEC(i);
- IF (procParent[i] = owner) AND ProcNameEq(VAL(CARDINAL, i), name) THEN
- RETURN i
- END
- END;
- owner := procParent[owner]
- END;
- i := VAL(INTEGER, nProcs) - 1;
- WHILE i > 0 DO
- DEC(i);
- IF (procParent[i] = -1) AND ProcNameEq(VAL(CARDINAL, i), name) THEN
- RETURN i
- END
- END;
- RETURN -1
- END FindProc;
- PROCEDURE FindProcByUid (uid: CARDINAL): INTEGER;
- (* Exact proc entry for a module-qualified call (M.P): the base name may
- be duplicated across modules, but the uid is unique. *)
- VAR i: INTEGER;
- BEGIN
- i := VAL(INTEGER, nProcs);
- WHILE i > 0 DO
- DEC(i);
- IF procUid[i] = uid THEN RETURN i END
- END;
- RETURN -1
- END FindProcByUid;
- PROCEDURE NoteImportedProc (name: ARRAY OF CHAR);
- (* FROM M IMPORT P (a procedure): remember the exact proc entry so an
- unqualified call resolves to the imported proc, not a same-named
- proc declared in another module. Reset at each unit. *)
- VAR pi: INTEGER;
- BEGIN
- IF SymTab.SymKind(name) # SymTab.KindProc THEN RETURN END;
- pi := FindProcByUid(SymTab.ProcUid(name));
- IF pi < 0 THEN RETURN END;
- IF nImps >= MaxImp THEN RETURN END;
- impProc[nImps] := pi;
- CopySpan(name, impName, 0, nImps * 64);
- INC(nImps)
- END NoteImportedProc;
- PROCEDURE Resolve (nm: ARRAY OF CHAR; VAR k: INTEGER;
- VAR t: SymTab.TypeIndex): BOOLEAN;
- VAR kind: INTEGER; ti: SymTab.TypeIndex;
- BEGIN
- IF FindSym(nm, kind, ti) THEN k := kind; t := ti; RETURN TRUE END;
- IF SymTab.Lookup(nm) THEN k := SymTab.SymKind(nm); t := SymTab.SymType(nm);
- RETURN TRUE
- END;
- (* A bare procedure used as a value: it lives in the proc table.
- Sibling method names must fall through so LowerDesign can bind
- THIS, so exclude methods of the open class implementation. *)
- IF FindProc(nm) >= 0 THEN
- IF (curImplCls = SymTab.InvalidType)
- OR NOT SymTab.MethodExists(curImplCls, nm) THEN
- k := SymTab.KindProc; t := SymTab.InvalidType; RETURN TRUE
- END
- END;
- RETURN FALSE
- END Resolve;
- PROCEDURE ProcInfo (nm: ARRAY OF CHAR; VAR uid: CARDINAL;
- VAR res: SymTab.TypeIndex; VAR depth: CARDINAL;
- VAR ext: BOOLEAN; VAR npar: CARDINAL): BOOLEAN;
- VAR i: INTEGER;
- BEGIN
- i := FindProc(nm);
- IF (i < 0) OR (i >= MaxProc) THEN RETURN FALSE END;
- uid := procUid[i]; res := procRes[i]; depth := procDep[i];
- ext := procExt[i]; npar := procNpar[i];
- IF npar > MaxPar THEN npar := MaxPar END;
- RETURN TRUE
- END ProcInfo;
- PROCEDURE ParamInfo (nm: ARRAY OF CHAR; i: CARDINAL;
- VAR t: SymTab.TypeIndex; VAR isVar: BOOLEAN;
- VAR pn: ARRAY OF CHAR): BOOLEAN;
- VAR k: INTEGER;
- BEGIN
- k := FindProc(nm);
- IF (k < 0) OR (k >= MaxProc) THEN RETURN FALSE END;
- IF (i >= procNpar[k]) OR (procNpar[k] > MaxPar) THEN RETURN FALSE END;
- t := parTy[VAL(CARDINAL, k) * MaxPar + i];
- isVar := parVar[VAL(CARDINAL, k) * MaxPar + i];
- CopySpan(parName, pn, VAL(CARDINAL, k) * MaxPar * 64 + i * 64, 0);
- RETURN TRUE
- END ParamInfo;
- PROCEDURE WithField (nm: ARRAY OF CHAR; VAR k: INTEGER;
- VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal): BOOLEAN;
- (* Resolves a bare ident as a field of an open WITH record (innermost
- first), leaving the field address in q and k=KindField. *)
- VAR wt: CARDINAL; fo: INTEGER; qb: QbeGen.QVal;
- BEGIN
- wt := withTop;
- WHILE wt > 0 DO
- DEC(wt);
- IF SymTab.FieldExists(withRec[wt], nm) THEN
- t := SymTab.FieldType(withRec[wt], nm);
- fo := SymTab.FieldOffset(withRec[wt], nm);
- k := SymTab.KindField;
- IF QbeGen.TopWith(qb) THEN QbeGen.FieldAddr(qb, fo, q)
- ELSE QbeGen.CopyOp("0", q)
- END;
- RETURN TRUE
- END
- END;
- RETURN FALSE
- END WithField;
- PROCEDURE Name (n: AST.Node; VAR s: ARRAY OF CHAR);
- BEGIN
- IF n = AST.NoNode THEN s[0] := CHR(0)
- ELSE AST.Txt(AST.Child(n, 0), s)
- END
- END Name;
- (* ---------------- compile-time set constants ----------------
- A SET CONST's value must be static data. Set literals are emitted as
- runtime temporaries, so the grammar computes the bit mask here (where
- the element expressions are already validated) and Lower re-emits it
- as a pooled descriptor (`QbeGen.PoolSet`). Only masks up to MaxCFoldW
- words (512 bits) are folded; a wider SET CONST stays a clear 230. *)
- PROCEDURE Pow2 (b: INTEGER): CARDINAL;
- VAR p: CARDINAL; k: INTEGER;
- BEGIN
- p := 1; k := 0;
- WHILE k < b DO p := p * 2; INC(k) END;
- RETURN p
- END Pow2;
- PROCEDURE MaskZero (VAR mask: ARRAY OF CARDINAL; words: CARDINAL);
- VAR i: CARDINAL;
- BEGIN
- i := 0;
- WHILE (i < words) AND (i <= HIGH(mask)) DO mask[i] := 0; INC(i) END
- END MaskZero;
- PROCEDURE MaskSetBit (VAR mask: ARRAY OF CARDINAL; bit: INTEGER);
- VAR w, b: INTEGER; p: CARDINAL; idx: CARDINAL;
- BEGIN
- IF (bit < 0) OR (bit >= VAL(INTEGER, MaxCFoldW) * 32) THEN RETURN END;
- w := bit DIV 32; b := bit MOD 32;
- IF w > VAL(INTEGER, HIGH(mask)) THEN RETURN END;
- idx := VAL(CARDINAL, w); p := Pow2(b);
- IF (mask[idx] DIV p) MOD 2 = 0 THEN mask[idx] := mask[idx] + p END
- END MaskSetBit;
- PROCEDURE MaskGetBit (VAR mask: ARRAY OF CARDINAL; bit: INTEGER): INTEGER;
- VAR w, b: INTEGER; p: CARDINAL;
- BEGIN
- IF (bit < 0) OR (bit >= VAL(INTEGER, MaxCFoldW) * 32) THEN RETURN 0 END;
- w := bit DIV 32; b := bit MOD 32;
- IF w > VAL(INTEGER, HIGH(mask)) THEN RETURN 0 END;
- p := Pow2(b);
- IF (mask[VAL(CARDINAL, w)] DIV p) MOD 2 = 1 THEN RETURN 1 END;
- RETURN 0
- END MaskGetBit;
- PROCEDURE EvalOrdConst (n: AST.Node; VAR v: INTEGER): BOOLEAN;
- (* The integer value of a compile-time ordinal expression, matching the
- grammar's folding (literals, named CONSTs, unary +/-, + - * DIV MOD). *)
- VAR k: INTEGER; s, cv: QbeGen.QVal; nm: SymTab.Name;
- a, b: INTEGER; isCh: BOOLEAN;
- BEGIN
- IF n = AST.NoNode THEN RETURN FALSE END;
- k := AST.Kind(n);
- IF k = AST.NkIntLit THEN
- Name(n, s); QbeGen.NormInt(s, cv); RETURN SymTab.ConstInt(cv, v)
- ELSIF k = AST.NkCharLit THEN
- Name(n, s);
- IF (s[0] = "'") OR (s[0] = '"') THEN
- v := QbeGen.CharVal(s); RETURN TRUE
- END;
- QbeGen.NormLit(s, cv, isCh); RETURN SymTab.ConstInt(cv, v)
- ELSIF k = AST.NkStrLit THEN
- (* a 1-character string literal "c" is a CHAR constant *)
- Name(n, s);
- IF SymTab.StrLen(s) = 3 THEN v := QbeGen.CharVal(s); RETURN TRUE END;
- RETURN FALSE
- ELSIF k = AST.NkDesignator THEN
- IF (AST.NChild(n) # 1) OR (AST.Kind(AST.Child(n, 0)) # AST.NkIdent) THEN
- RETURN FALSE END;
- Name(AST.Child(n, 0), nm);
- IF SymTab.Equal(nm, "TRUE") THEN v := 1; RETURN TRUE END;
- IF SymTab.Equal(nm, "FALSE") THEN v := 0; RETURN TRUE END;
- IF SymTab.Lookup(nm) AND (SymTab.SymKind(nm) = SymTab.KindConst)
- AND SymTab.GetSymVal(nm, cv) AND QbeGen.IsImm(cv) THEN
- RETURN SymTab.ConstInt(cv, v)
- END;
- RETURN FALSE
- ELSIF k = AST.NkUnary THEN
- IF AST.Op(n) = AST.OpSub THEN
- IF EvalOrdConst(AST.Child(n, 0), v) THEN v := -v; RETURN TRUE END
- ELSIF AST.Op(n) = AST.OpAdd THEN
- RETURN EvalOrdConst(AST.Child(n, 0), v)
- END;
- RETURN FALSE
- ELSIF k = AST.NkBinExpr THEN
- IF EvalOrdConst(AST.Child(n, 0), a)
- AND EvalOrdConst(AST.Child(n, 1), b) THEN
- IF AST.Op(n) = AST.OpAdd THEN v := a + b
- ELSIF AST.Op(n) = AST.OpSub THEN v := a - b
- ELSIF AST.Op(n) = AST.OpMul THEN v := a * b
- ELSIF AST.Op(n) = AST.OpDiv THEN
- IF b = 0 THEN RETURN FALSE END; v := a DIV b
- ELSIF AST.Op(n) = AST.OpMod THEN
- IF b = 0 THEN RETURN FALSE END; v := a MOD b
- ELSE RETURN FALSE
- END;
- RETURN TRUE
- END;
- RETURN FALSE
- END;
- RETURN FALSE
- END EvalOrdConst;
- PROCEDURE EvalSetElem (el: AST.Node; VAR mask: ARRAY OF CARDINAL;
- lo: INTEGER; span: CARDINAL): BOOLEAN;
- VAR a, b: INTEGER;
- BEGIN
- IF AST.Kind(el) = AST.NkSubrange THEN
- IF NOT EvalOrdConst(AST.Child(el, 0), a)
- OR NOT EvalOrdConst(AST.Child(el, 1), b) THEN RETURN FALSE END;
- IF (a < lo) OR (b < lo) OR (a > b)
- OR (b >= lo + VAL(INTEGER, span)) THEN RETURN FALSE END;
- WHILE a <= b DO MaskSetBit(mask, a - lo); INC(a) END;
- RETURN TRUE
- END;
- IF NOT EvalOrdConst(el, a) THEN RETURN FALSE END;
- IF (a < lo) OR (a >= lo + VAL(INTEGER, span)) THEN RETURN FALSE END;
- MaskSetBit(mask, a - lo);
- RETURN TRUE
- END EvalSetElem;
- PROCEDURE NoteSetMask (name: ARRAY OF CHAR; mask: ARRAY OF CARDINAL;
- words: CARDINAL; lo: INTEGER);
- VAR i: CARDINAL;
- BEGIN
- IF cfoldN >= MaxCFold THEN RETURN END;
- CopySpan(name, cfoldName, 0, cfoldN * 64);
- cfoldW[cfoldN] := words; cfoldLo[cfoldN] := lo;
- i := 0;
- WHILE i < MaxCFoldW DO
- IF (i < words) AND (i <= HIGH(mask)) THEN
- cfoldMask[cfoldN * MaxCFoldW + i] := mask[i]
- ELSE cfoldMask[cfoldN * MaxCFoldW + i] := 0
- END;
- INC(i)
- END;
- INC(cfoldN)
- END NoteSetMask;
- PROCEDURE LookupSetMask (name: ARRAY OF CHAR; VAR mask: ARRAY OF CARDINAL;
- VAR words: CARDINAL; VAR lo: INTEGER): BOOLEAN;
- VAR i: INTEGER; k: CARDINAL;
- BEGIN
- i := VAL(INTEGER, cfoldN) - 1;
- WHILE i >= 0 DO
- IF SpanEq(cfoldName, VAL(CARDINAL, i) * 64, name) THEN
- words := cfoldW[i]; lo := cfoldLo[i];
- k := 0;
- WHILE (k < words) AND (k <= HIGH(mask)) DO
- mask[k] := cfoldMask[VAL(CARDINAL, i) * MaxCFoldW + k]; INC(k)
- END;
- RETURN TRUE
- END;
- DEC(i)
- END;
- RETURN FALSE
- END LookupSetMask;
- PROCEDURE EvalSetConst (n: AST.Node; VAR mask: ARRAY OF CARDINAL;
- VAR words: CARDINAL; VAR lo: INTEGER): BOOLEAN;
- VAR k, i, a, b, r: INTEGER;
- t: SymTab.TypeIndex;
- span: CARDINAL;
- seq, el, next: AST.Node;
- m1, m2: ARRAY [0 .. MaxCFoldW - 1] OF CARDINAL;
- w1, w2: CARDINAL;
- l1, l2: INTEGER;
- op: INTEGER;
- nm: SymTab.Name;
- BEGIN
- IF n = AST.NoNode THEN RETURN FALSE END;
- k := AST.Kind(n);
- IF (k = AST.NkSetLit) OR (k = AST.NkBraceLit) THEN
- t := AST.Ty(n);
- IF (k = AST.NkBraceLit) AND (SymTab.ClassOf(t) # SymTab.ClSet) THEN
- RETURN FALSE
- END;
- IF t = SymTab.InvalidType THEN
- lo := 0; words := 8; span := 256
- ELSE
- lo := SymTab.SetBaseLo(t); words := SymTab.SetWords(t);
- span := SymTab.SetCount(t);
- IF words = 0 THEN words := 8; span := 256; lo := 0 END
- END;
- IF words > MaxCFoldW THEN RETURN FALSE END;
- MaskZero(mask, words);
- IF k = AST.NkSetLit THEN
- i := 0;
- WHILE i < VAL(INTEGER, AST.NChild(n)) DO
- el := AST.Child(n, VAL(CARDINAL, i));
- IF NOT EvalSetElem(el, mask, lo, span) THEN RETURN FALSE END;
- INC(i)
- END
- ELSE
- seq := n;
- WHILE seq # AST.NoNode DO
- i := 0;
- WHILE i < VAL(INTEGER, AST.NChild(seq)) DO
- el := AST.Child(seq, VAL(CARDINAL, i));
- IF AST.Kind(el) # AST.NkBlock THEN
- IF NOT EvalSetElem(el, mask, lo, span) THEN RETURN FALSE END
- END;
- INC(i)
- END;
- next := AST.NoNode;
- IF AST.NChild(seq) > 0 THEN
- next := AST.Child(seq, AST.NChild(seq) - 1);
- IF AST.Kind(next) # AST.NkBlock THEN next := AST.NoNode END
- END;
- seq := next
- END
- END;
- RETURN TRUE
- ELSIF k = AST.NkDesignator THEN
- IF (AST.NChild(n) # 1) OR (AST.Kind(AST.Child(n, 0)) # AST.NkIdent) THEN
- RETURN FALSE END;
- Name(AST.Child(n, 0), nm);
- RETURN LookupSetMask(nm, mask, words, lo)
- ELSIF k = AST.NkBinExpr THEN
- op := AST.Op(n);
- IF (op # AST.OpAdd) AND (op # AST.OpSub)
- AND (op # AST.OpMul) AND (op # AST.OpDiv) THEN
- RETURN FALSE END;
- IF NOT EvalSetConst(AST.Child(n, 0), m1, w1, l1)
- OR NOT EvalSetConst(AST.Child(n, 1), m2, w2, l2) THEN
- RETURN FALSE END;
- IF l1 # l2 THEN RETURN FALSE END;
- lo := l1;
- words := w1; IF w2 > words THEN words := w2 END;
- IF words > MaxCFoldW THEN RETURN FALSE END;
- MaskZero(mask, words);
- i := 0;
- WHILE i < VAL(INTEGER, words) * 32 DO
- a := MaskGetBit(m1, i); b := MaskGetBit(m2, i);
- IF op = AST.OpAdd THEN r := a; IF b = 1 THEN r := 1 END
- ELSIF op = AST.OpMul THEN
- IF (a = 1) AND (b = 1) THEN r := 1 ELSE r := 0 END
- ELSIF op = AST.OpSub THEN r := a; IF b = 1 THEN r := 0 END
- ELSE r := (a + b) MOD 2
- END;
- IF r # 0 THEN MaskSetBit(mask, i) END;
- INC(i)
- END;
- RETURN TRUE
- END;
- RETURN FALSE
- END EvalSetConst;
- PROCEDURE SetConstMaskOf (n: AST.Node; VAR mask: ARRAY OF CARDINAL;
- VAR words: CARDINAL; VAR lo: INTEGER): BOOLEAN;
- BEGIN
- words := 0; lo := 0;
- RETURN EvalSetConst(n, mask, words, lo)
- END SetConstMaskOf;
- PROCEDURE ParamName (proc: ARRAY OF CHAR; i: CARDINAL;
- VAR s: ARRAY OF CHAR);
- VAR t: SymTab.TypeIndex; isV: BOOLEAN;
- BEGIN
- IF NOT ParamInfo(proc, i, t, isV, s) THEN s[0] := CHR(0) END
- END ParamName;
- PROCEDURE IsArith (op: INTEGER): BOOLEAN;
- BEGIN
- RETURN (op = AST.OpAdd) OR (op = AST.OpSub) OR (op = AST.OpMul)
- OR (op = AST.OpDiv) OR (op = AST.OpMod)
- END IsArith;
- PROCEDURE IsRel (op: INTEGER): BOOLEAN;
- BEGIN
- RETURN ((op >= AST.OpEq) AND (op <= AST.OpGe)) OR (op = AST.OpIn)
- END IsRel;
- PROCEDURE RelSym (a: INTEGER; VAR s: INTEGER): BOOLEAN;
- BEGIN
- IF a = AST.OpEq THEN s := SymTab.OpEq
- ELSIF a = AST.OpNe THEN s := SymTab.OpNeq1
- ELSIF a = AST.OpLt THEN s := SymTab.OpLt
- ELSIF a = AST.OpLe THEN s := SymTab.OpLe
- ELSIF a = AST.OpGt THEN s := SymTab.OpGt
- ELSIF a = AST.OpGe THEN s := SymTab.OpGe
- ELSE RETURN FALSE
- END;
- RETURN TRUE
- END RelSym;
- PROCEDURE IsScalarClass (cls: INTEGER): BOOLEAN;
- BEGIN
- RETURN (cls = SymTab.ClInt) OR (cls = SymTab.ClBool)
- OR (cls = SymTab.ClChar) OR (cls = SymTab.ClUChar)
- OR (cls = SymTab.ClEnum)
- END IsScalarClass;
- PROCEDURE IsSimpleScalar (n: AST.Node): BOOLEAN;
- VAR nm: SymTab.Name; k: INTEGER; t: SymTab.TypeIndex;
- BEGIN
- IF (AST.Kind(n) # AST.NkDesignator) OR (AST.NChild(n) # 1) THEN
- RETURN FALSE END;
- IF AST.Kind(AST.Child(n, 0)) # AST.NkIdent THEN RETURN FALSE END;
- Name(AST.Child(n, 0), nm);
- IF NOT Resolve(nm, k, t) THEN RETURN FALSE END;
- IF (k # SymTab.KindVar) AND (k # SymTab.KindParam)
- AND (k # SymTab.KindConst) THEN
- RETURN FALSE END;
- IF SymTab.Equal(nm, "TRUE") OR SymTab.Equal(nm, "FALSE") THEN
- RETURN TRUE END;
- RETURN IsScalarClass(SymTab.ClassOf(t))
- END IsSimpleScalar;
- PROCEDURE CalleeName (call: AST.Node; VAR nm: SymTab.Name);
- (* The callee of NkCall: child 0 is a designator (or bare ident). *)
- VAR d: AST.Node;
- BEGIN
- nm[0] := CHR(0);
- d := AST.Child(call, 0);
- IF d = AST.NoNode THEN RETURN END;
- IF AST.Kind(d) = AST.NkDesignator THEN d := AST.Child(d, 0) END;
- IF (d # AST.NoNode) AND (AST.Kind(d) = AST.NkIdent) THEN Name(d, nm) END
- END CalleeName;
- PROCEDURE CallResultType (call: AST.Node; VAR t: SymTab.TypeIndex): BOOLEAN;
- (* The result type of a call used as a designator base (F().M()). *)
- VAR nm: SymTab.Name; uid, depth, np: CARDINAL; res: SymTab.TypeIndex;
- ext: BOOLEAN; pi: INTEGER;
- BEGIN
- t := SymTab.InvalidType;
- CalleeName(call, nm);
- pi := FindProc(nm);
- IF pi >= 0 THEN
- IF ProcInfoIdx(pi, uid, res, depth, ext, np) THEN t := res; RETURN TRUE END;
- RETURN FALSE
- END;
- IF SymTab.Lookup(nm) AND (SymTab.SymKind(nm) = SymTab.KindProc) THEN
- t := SymTab.ProcRes(nm); RETURN TRUE
- END;
- RETURN FALSE
- END CallResultType;
- PROCEDURE IsMethodCall (n: AST.Node): BOOLEAN;
- (* n is a call whose callee resolves to a class method: `obj.Method`,
- `p^.Method`, or a bare sibling name inside an implementation. *)
- VAR d, sel: AST.Node; nm, fn: SymTab.Name; k: INTEGER; t: SymTab.TypeIndex;
- i: CARDINAL;
- BEGIN
- IF (AST.Kind(n) # AST.NkCall) OR (AST.NChild(n) < 1) THEN RETURN FALSE END;
- d := AST.Child(n, 0);
- IF AST.Kind(d) # AST.NkDesignator THEN RETURN FALSE END;
- IF AST.NChild(d) = 1 THEN
- (* bare name: a sibling method inside a class implementation? *)
- Name(AST.Child(d, 0), nm);
- RETURN (curImplCls # SymTab.InvalidType)
- AND SymTab.MethodExists(curImplCls, nm)
- END;
- IF AST.Kind(AST.Child(d, 0)) = AST.NkCall THEN
- (* F().M(): the base is a call; take its result type *)
- IF NOT CallResultType(AST.Child(d, 0), t) THEN RETURN FALSE END
- ELSE
- Name(AST.Child(d, 0), nm);
- IF NOT Resolve(nm, k, t) THEN RETURN FALSE END
- END;
- i := 1;
- WHILE i < AST.NChild(d) DO
- sel := AST.Child(d, i);
- IF AST.Op(sel) = AST.SelField THEN
- Name(AST.Child(sel, 0), fn);
- IF (t # SymTab.InvalidType) AND (SymTab.ClassOf(t) = SymTab.ClClass)
- AND SymTab.MethodExists(t, fn) THEN
- RETURN i = AST.NChild(d) - 1
- END;
- IF (t = SymTab.InvalidType) OR NOT SymTab.FieldExists(t, fn) THEN
- RETURN FALSE
- END;
- t := SymTab.FieldType(t, fn)
- ELSIF AST.Op(sel) = AST.SelIndex THEN
- IF (t = SymTab.InvalidType) OR (SymTab.ClassOf(t) # SymTab.ClArray) THEN
- RETURN FALSE
- END;
- t := SymTab.ArrayElem(t)
- ELSIF AST.Op(sel) = AST.SelDeref THEN
- IF (t = SymTab.InvalidType) OR (SymTab.ClassOf(t) # SymTab.ClPtr) THEN
- RETURN FALSE
- END;
- t := SymTab.PtrBase(t)
- ELSE RETURN FALSE
- END;
- INC(i)
- END;
- RETURN FALSE
- END IsMethodCall;
- PROCEDURE CalleeAlias (call: AST.Node; VAR mal: SymTab.Name): BOOLEAN;
- (* Callee is `M.P` (module-qualified): materialize the export alias. *)
- VAR d, sel: AST.Node; nm, fn, al: SymTab.Name; k: INTEGER;
- t: SymTab.TypeIndex;
- BEGIN
- d := AST.Child(call, 0);
- IF (AST.Kind(d) # AST.NkDesignator) OR (AST.NChild(d) # 2) THEN
- RETURN FALSE
- END;
- sel := AST.Child(d, 1);
- IF AST.Op(sel) # AST.SelField THEN RETURN FALSE END;
- Name(AST.Child(d, 0), nm);
- IF NOT Resolve(nm, k, t) THEN RETURN FALSE END;
- IF k # SymTab.KindModule THEN RETURN FALSE END;
- Name(AST.Child(sel, 0), fn);
- IF NOT SymTab.MaterializeAlias(nm, fn, al) THEN RETURN FALSE END;
- (* Lower's proc table is keyed by the base name (the mangled symbol
- is <base>_<uid>), so return fn, not the materialized alias. The
- uid comes from the alias, which is module-exact. *)
- qualIsVar := SymTab.SymKind(al) # SymTab.KindProc;
- qualUid := SymTab.ProcUid(al);
- CopyN(fn, mal);
- RETURN TRUE
- END CalleeAlias;
- PROCEDURE FinalLoad (t: SymTab.TypeIndex; sfx: BOOLEAN; VAR q: QbeGen.QVal);
- (* Fact's final load after a designator/result suffix: scalars/pointers
- are loaded from their address; aggregates keep the address. *)
- VAR cls: INTEGER; q2: QbeGen.QVal;
- BEGIN
- IF NOT sfx THEN RETURN END;
- IF t = SymTab.InvalidType THEN QbeGen.CopyOp("0", q); RETURN END;
- cls := SymTab.ClassOf(t);
- IF (cls # SymTab.ClRecord) AND (cls # SymTab.ClSet)
- AND (cls # SymTab.ClArray) AND (cls # SymTab.ClClass) THEN
- QbeGen.ElemLoad(q, t, q2); QbeGen.CopyOp(q2, q)
- END
- END FinalLoad;
- PROCEDURE LowerDesign (n: AST.Node; VAR t: SymTab.TypeIndex; VAR k: INTEGER;
- VAR q: QbeGen.QVal; VAR qn: SymTab.Name;
- VAR sfx: BOOLEAN);
- (* Base ident then selectors (mirrors the grammar's Design). Leaves the
- address in q with sfx=TRUE for selectors; base vars/consts load. *)
- VAR nm, fn, mal: SymTab.Name; cv: QbeGen.QVal; cls, op: INTEGER;
- i: CARDINAL; sel: AST.Node; eT, it: SymTab.TypeIndex;
- iq, ql, qlo, qhi, qe: QbeGen.QVal;
- lo, hi, fo: INTEGER; isOpen: BOOLEAN;
- BEGIN
- IF AST.Kind(AST.Child(n, 0)) = AST.NkCall THEN
- (* a call result used as a designator base: P()^, P()^.field,
- A()[i]. The call yields a value (a pointer/record address);
- sfx=FALSE so a following ^ does not reload it. *)
- LowerExpr(AST.Child(n, 0), t, q);
- k := -1; qn[0] := CHR(0); sfx := FALSE
- ELSE
- Name(AST.Child(n, 0), nm);
- QbeGen.CopyOp(nm, qn);
- sfx := FALSE;
- IF NOT Resolve(nm, k, t) THEN
- IF WithField(nm, k, t, q) THEN
- sfx := TRUE
- ELSIF (curImplCls # SymTab.InvalidType)
- AND SymTab.MethodExists(curImplCls, nm) THEN
- (* bare sibling method name inside a class implementation *)
- QbeGen.ThisBase(q);
- QbeGen.ArmRecv(q);
- curMethCls := curImplCls; k := SymTab.KindProc;
- t := SymTab.InvalidType; sfx := FALSE
- ELSE
- t := SymTab.InvalidType; k := -1; QbeGen.CopyOp("0", q); RETURN
- END
- END;
- IF k = SymTab.KindConst THEN
- IF SymTab.Equal(nm, "TRUE") THEN
- t := SymTab.BoolType(); QbeGen.CopyOp("1", q)
- ELSIF SymTab.Equal(nm, "FALSE") THEN
- t := SymTab.BoolType(); QbeGen.CopyOp("0", q)
- ELSIF SymTab.Equal(nm, "NIL") THEN
- QbeGen.CopyOp("0", q)
- ELSE
- cls := SymTab.ClassOf(t);
- IF (cls = SymTab.ClArray) OR (cls = SymTab.ClRecord)
- OR (cls = SymTab.ClClass) OR (cls = SymTab.ClStr)
- OR (cls = SymTab.ClUStr) OR (cls = SymTab.ClSet) THEN
- (* aggregate/string const: its value IS the descriptor address *)
- IF GetConstVal(nm, cv) THEN QbeGen.CopyOp(cv, q)
- ELSE QbeGen.CopyOp("0", q)
- END
- ELSIF (cls = SymTab.ClInt) OR (cls = SymTab.ClChar)
- OR (cls = SymTab.ClEnum) OR (cls = SymTab.ClLong)
- OR (cls = SymTab.ClBool) THEN
- IF GetConstVal(nm, cv) AND QbeGen.IsImm(cv) THEN
- QbeGen.CopyOp(cv, q)
- ELSE QbeGen.CopyOp("0", q)
- END
- ELSIF cls = SymTab.ClReal THEN
- (* a real CONST lives in its data symbol; load it *)
- QbeGen.LoadVar(nm, TRUE, q)
- ELSE QbeGen.CopyOp("0", q)
- END
- END
- ELSIF (k = SymTab.KindVar) OR (k = SymTab.KindParam) THEN
- cls := SymTab.ClassOf(t);
- IF AST.Op(n) = 1 THEN
- (* a forward module-level reference: the grammar's FwdAddrOper
- leaves an address (sfx=TRUE), not a loaded value *)
- QbeGen.AddrOf(nm, q); sfx := TRUE
- ELSIF (cls = SymTab.ClInt) OR (cls = SymTab.ClBool)
- OR (cls = SymTab.ClChar) OR (cls = SymTab.ClUChar)
- OR (cls = SymTab.ClEnum) OR (cls = SymTab.ClReal) THEN
- QbeGen.LoadVar(nm, cls = SymTab.ClReal, q)
- ELSIF (cls = SymTab.ClPtr) OR (cls = SymTab.ClProc) THEN
- QbeGen.LoadPtr(nm, q)
- ELSIF cls = SymTab.ClLong THEN
- QbeGen.LoadLong(nm, q)
- ELSE QbeGen.AddrOf(nm, q)
- END
- ELSIF k = SymTab.KindField THEN
- (* q already holds the field address (WithField) *)
- ELSE QbeGen.CopyOp("0", q)
- END;
- END;
- i := 1;
- WHILE i < AST.NChild(n) DO
- sel := AST.Child(n, i);
- op := AST.Op(sel);
- IF op = AST.SelField THEN
- Name(AST.Child(sel, 0), fn);
- IF k = SymTab.KindModule THEN
- (* qualified L.x: materialize the export, then load it *)
- IF SymTab.MaterializeAlias(nm, fn, mal) THEN
- QbeGen.CopyOp(mal, qn);
- t := SymTab.SymType(mal); k := SymTab.SymKind(mal); sfx := FALSE;
- IF k = SymTab.KindProc THEN QbeGen.CopyOp("0", q)
- ELSIF NOT QbeGen.LoadDesignator(mal, t, k, q) THEN
- t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
- END
- ELSE t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
- END
- ELSIF t = SymTab.InvalidType THEN
- ELSIF (SymTab.ClassOf(t) = SymTab.ClClass)
- AND SymTab.MethodExists(t, fn) THEN
- (* obj.Method: bind the receiver; the call supplies the value *)
- QbeGen.ArmRecv(q);
- QbeGen.CopyOp(fn, qn);
- curMethCls := t; k := SymTab.KindProc; t := SymTab.InvalidType
- ELSIF NOT SymTab.FieldExists(t, fn) THEN t := SymTab.InvalidType
- ELSE
- fo := SymTab.FieldOffset(t, fn);
- t := SymTab.FieldType(t, fn);
- QbeGen.FieldAddr(q, fo, qe);
- QbeGen.CopyOp(qe, q);
- sfx := TRUE
- END
- ELSIF op = AST.SelIndex THEN
- LowerExpr(AST.Child(sel, 0), it, iq);
- IF t = SymTab.InvalidType THEN
- ELSE
- QbeGen.WidenIndex(iq, ql);
- isOpen := SymTab.IsOpenArray(t);
- IF isOpen THEN
- QbeGen.CopyOp("0", qlo);
- IF SymTab.IsCharArray(t) OR SymTab.IsUCharArray(t) THEN
- QbeGen.OpenHiChar(q, qhi)
- ELSE QbeGen.OpenHi(q, qhi)
- END
- ELSE
- lo := SymTab.ArrayLo(t); hi := SymTab.ArrayHi(t);
- IF SymTab.IsCharArray(t) OR SymTab.IsUCharArray(t) THEN
- hi := hi + 1
- END;
- QbeGen.IntStr(lo, qlo); QbeGen.IntStr(hi, qhi)
- END;
- QbeGen.CheckRange(ql, qlo, qhi);
- eT := SymTab.ArrayElem(t);
- QbeGen.ElemAddr(q, ql, qlo, t, qe);
- IF SymTab.ClassOf(eT) = SymTab.ClArray THEN
- QbeGen.ElemLoad(qe, eT, q)
- ELSE QbeGen.CopyOp(qe, q)
- END;
- t := eT; sfx := TRUE
- END
- ELSIF op = AST.SelDeref THEN
- IF t = SymTab.InvalidType THEN
- ELSIF SymTab.ClassOf(t) # SymTab.ClPtr THEN t := SymTab.InvalidType
- ELSE
- IF sfx THEN QbeGen.ElemLoad(q, t, qe); QbeGen.CopyOp(qe, q) END;
- t := SymTab.PtrBase(t); sfx := TRUE
- END
- END;
- INC(i)
- END
- END LowerDesign;
- PROCEDURE CompositeRes (t: SymTab.TypeIndex): BOOLEAN;
- (* TRUE for a composite (by-value) function result: record/array/set.
- Such results use a hidden destination pointer (sret). *)
- VAR cls: INTEGER;
- BEGIN
- IF t = SymTab.InvalidType THEN RETURN FALSE END;
- cls := SymTab.ClassOf(t);
- RETURN (cls = SymTab.ClRecord) OR (cls = SymTab.ClArray)
- OR (cls = SymTab.ClSet) OR (cls = SymTab.ClClass)
- END CompositeRes;
- PROCEDURE UserLabel (name: ARRAY OF CHAR; VAR q: QbeGen.QVal);
- (* The QbeGen label for a user label/goto name (per procedure). *)
- VAR i: CARDINAL;
- BEGIN
- i := 0;
- WHILE i < nUserLab DO
- IF SpanEq(userLabNm, i * 64, name) THEN q := userLabQ[i]; RETURN END;
- INC(i)
- END;
- QbeGen.NewLabel(q);
- IF nUserLab <= HIGH(userLabQ) THEN
- CopySpan(name, userLabNm, 0, nUserLab * 64);
- userLabQ[nUserLab] := q;
- INC(nUserLab)
- END
- END UserLabel;
- PROCEDURE LowerCall (n: AST.Node; VAR t: SymTab.TypeIndex;
- VAR q: QbeGen.QVal; want: BOOLEAN);
- VAR nm, pn, cqn: SymTab.Name; i, ai: CARDINAL; at, ft, res: SymTab.TypeIndex;
- qa, qt, qe, mg, qres: QbeGen.QVal; conv, isV, qual, ind, isC, va: BOOLEAN;
- uid, depth, np: CARDINAL; ext: BOOLEAN; ck: INTEGER; pi: INTEGER;
- ct: SymTab.TypeIndex; csfx: BOOLEAN; cl: CHAR;
- seq, next, arg: AST.Node;
- BEGIN
- qual := CalleeAlias(n, nm);
- ind := FALSE; isC := FALSE; va := FALSE; pi := -1; ext := FALSE;
- IF qual AND qualIsVar THEN
- (* M.Error where Error is a procedure-typed variable: indirect. *)
- LowerDesign(AST.Child(n, 0), ct, ck, qa, cqn, csfx);
- IF (ct # SymTab.InvalidType)
- AND (SymTab.ClassOf(ct) = SymTab.ClProc) THEN
- ind := TRUE;
- res := SymTab.ProcTypeRes(ct);
- np := SymTab.ProcTypeNPar(ct);
- QbeGen.CallBeginInd(qa, res, FALSE)
- ELSE
- res := SymTab.InvalidType;
- QbeGen.CallBegin(nm, res, 0, FALSE)
- END
- ELSIF qual THEN
- (* M.P: the base name may be duplicated across modules, so select
- the exact proc entry by the alias's uid. *)
- pi := FindProcByUid(qualUid);
- IF ProcInfoIdx(pi, uid, res, depth, ext, np) THEN
- IF ext THEN QbeGen.CopyOp(nm, mg) ELSE QbeGen.Mangled(nm, uid, mg) END;
- QbeGen.CallBegin(mg, res, depth, ext)
- ELSE
- res := SymTab.InvalidType;
- QbeGen.CallBegin(nm, res, 0, FALSE)
- END
- ELSE
- CalleeName(n, nm);
- IF FindSym(nm, ck, ct)
- AND ((ck = SymTab.KindVar) OR (ck = SymTab.KindParam))
- AND (SymTab.ClassOf(ct) = SymTab.ClProc) THEN
- (* indirect call through a procedure-typed variable *)
- ind := TRUE;
- LowerDesign(AST.Child(n, 0), ct, ck, qa, cqn, csfx);
- res := SymTab.ProcTypeRes(ct);
- np := SymTab.ProcTypeNPar(ct);
- QbeGen.CallBeginInd(qa, res, FALSE)
- ELSIF ProcInfo(nm, uid, res, depth, ext, np) THEN
- pi := FindProc(nm);
- IF ext THEN
- (* External callee: bind to its C link name (the procedure's
- own name); SymTab.ProcLink cannot be consulted after
- EndUnit. *)
- QbeGen.CopyOp(nm, mg)
- ELSE
- QbeGen.Mangled(nm, uid, mg)
- END;
- QbeGen.CallBegin(mg, res, depth, ext)
- ELSE
- (* not in Lower's table: a predef C proc (SYSTEM.TRANSFER,
- NEWPROCESS, ...) lives in SymTab with a C link name *)
- IF SymTab.Lookup(nm) AND (SymTab.SymKind(nm) = SymTab.KindProc) THEN
- isC := TRUE;
- res := SymTab.ProcRes(nm);
- np := SymTab.ProcNPar(nm);
- IF SymTab.ProcLink(nm, pn) THEN
- QbeGen.CallBegin(pn, res, 0, TRUE)
- ELSE
- QbeGen.CallBegin(nm, res, 0, FALSE)
- END
- ELSE
- res := SymTab.InvalidType;
- QbeGen.CallBegin(nm, res, 0, FALSE)
- END
- END
- END;
- IF CompositeRes(res) THEN
- (* composite result: pass a hidden destination pointer first *)
- QbeGen.AllocLocal(res, qres);
- IF NOT QbeGen.CallArg(qres, "l") THEN END
- END;
- va := (pi >= 0) AND (pi < VAL(INTEGER, nProcs)) AND procVa[pi];
- (* actuals are a chunked sequence (AstCallNode): follow the NkBlock
- continuations so a call with >MaxChild-1 arguments is complete *)
- ai := 0;
- seq := n;
- WHILE seq # AST.NoNode DO
- i := 0;
- IF seq = n THEN i := 1 END; (* skip the callee in the head *)
- WHILE i < AST.NChild(seq) DO
- arg := AST.Child(seq, i);
- IF AST.Kind(arg) # AST.NkBlock THEN
- LowerExpr(arg, at, qe);
- IF ind THEN
- ft := SymTab.ProcTypeParamType(ct, ai);
- isV := SymTab.ProcTypeParamIsVar(ct, ai)
- ELSIF va THEN
- ft := at; isV := FALSE
- ELSIF isC THEN
- ft := SymTab.ParamType(nm, ai);
- isV := SymTab.ParamIsVar(nm, ai)
- ELSE
- IF NOT ParamInfoIdx(pi, ai, ft, isV, pn) THEN END
- END;
- IF va THEN
- (* permissive C variadic argument *)
- QbeGen.CArgAdd(qe, at, qa, cl)
- ELSIF isV THEN
- IF (SymTab.ClassOf(at) = SymTab.ClChar)
- AND (SymTab.ClassOf(ft) = SymTab.ClArray)
- AND (SymTab.ClassOf(SymTab.ArrayElem(ft)) = SymTab.ClChar)
- AND QbeGen.IsImm(qe) THEN
- (* 1-char string literal passed to a VAR ARRAY OF CHAR:
- pass its static descriptor *)
- QbeGen.DeclCharStr(qe, qa);
- IF NOT QbeGen.CallArg(qa, "l") THEN END
- ELSIF QbeGen.AddrOfVal(qe, qa) THEN
- IF NOT QbeGen.CallArg(qa, "l") THEN END
- END
- ELSE
- IF (SymTab.ClassOf(at) = SymTab.ClChar)
- AND (SymTab.ClassOf(ft) = SymTab.ClArray)
- AND (SymTab.ClassOf(SymTab.ArrayElem(ft)) = SymTab.ClChar)
- AND QbeGen.IsImm(qe) THEN
- (* 1-char string literal passed to ARRAY OF CHAR: pass its
- static descriptor, not the char code *)
- QbeGen.DeclCharStr(qe, qa);
- IF NOT QbeGen.CallArg(qa, "l") THEN END
- ELSE
- conv := (SymTab.ClassOf(ft) = SymTab.ClReal)
- AND SymTab.IsIntFamily(at);
- IF conv THEN
- QbeGen.ConvIR(qe, qt);
- IF NOT QbeGen.CallArg(qt, "d") THEN END
- ELSE
- IF NOT QbeGen.CallArg(qe, QbeGen.ArgClass(ft)) THEN END
- END
- END
- END;
- INC(ai)
- END;
- INC(i)
- END;
- next := AST.NoNode;
- IF AST.NChild(seq) > 0 THEN
- next := AST.Child(seq, AST.NChild(seq) - 1);
- IF AST.Kind(next) # AST.NkBlock THEN next := AST.NoNode END
- END;
- seq := next
- END;
- IF want THEN
- t := res; QbeGen.CallEnd(TRUE, q);
- IF CompositeRes(res) THEN QbeGen.CopyOp(qres, q) END
- ELSE
- t := SymTab.InvalidType; QbeGen.CopyOp("0", q); QbeGen.CallEnd(FALSE, q)
- END
- END LowerCall;
- PROCEDURE IsBuiltin (nm: ARRAY OF CHAR): BOOLEAN;
- BEGIN
- RETURN SymTab.Equal(nm, "ABS") OR SymTab.Equal(nm, "ODD")
- OR SymTab.Equal(nm, "CAP")
- OR SymTab.Equal(nm, "CHR") OR SymTab.Equal(nm, "ORD")
- OR SymTab.Equal(nm, "UCHR") OR SymTab.Equal(nm, "CHR8")
- OR SymTab.Equal(nm, "UORD")
- OR SymTab.Equal(nm, "SIZE") OR SymTab.Equal(nm, "TSIZE")
- OR SymTab.Equal(nm, "LEN") OR SymTab.Equal(nm, "HIGH")
- OR SymTab.Equal(nm, "VAL")
- OR SymTab.Equal(nm, "MIN") OR SymTab.Equal(nm, "MAX")
- OR SymTab.Equal(nm, "SHIFT") OR SymTab.Equal(nm, "ROTATE")
- OR SymTab.Equal(nm, "ADR")
- OR SymTab.Equal(nm, "INC") OR SymTab.Equal(nm, "DEC")
- OR SymTab.Equal(nm, "INCL") OR SymTab.Equal(nm, "EXCL")
- OR SymTab.Equal(nm, "NEW") OR SymTab.Equal(nm, "DISPOSE")
- END IsBuiltin;
- PROCEDURE LowerBuiltinExpr (n: AST.Node; nm: SymTab.Name;
- VAR t: SymTab.TypeIndex; VAR q: QbeGen.QVal);
- (* Value builtins (Fact). *)
- VAR et, et2, dt, vt: SymTab.TypeIndex; qa, qt, q2, qd, qr: QbeGen.QVal;
- k, lo, hi, c1, c2, ucp: INTEGER; qn, vn: SymTab.Name; sfx: BOOLEAN;
- isMax, isU, uok: BOOLEAN;
- BEGIN
- IF SymTab.Equal(nm, "ODD") THEN
- LowerExpr(AST.Child(n, 1), et, q);
- QbeGen.NewTemp(qt);
- QbeGen.Op3("and", qt, q, "1", FALSE); (* x & 1 *)
- QbeGen.NewTemp(qa);
- QbeGen.Cmp(SymTab.OpNeq1, qt, "0", qa, FALSE); (* <> 0 *)
- QbeGen.CopyOp(qa, q); t := SymTab.BoolType()
- ELSIF SymTab.Equal(nm, "ABS") THEN
- LowerExpr(AST.Child(n, 1), et, q);
- QbeGen.AbsQ(q, qa, SymTab.ClassOf(et) = SymTab.ClReal);
- QbeGen.CopyOp(qa, q); t := et
- ELSIF SymTab.Equal(nm, "CAP") THEN
- LowerExpr(AST.Child(n, 1), et, q);
- QbeGen.CapQ(q, qa); QbeGen.CopyOp(qa, q); t := SymTab.CharType()
- ELSIF SymTab.Equal(nm, "CHR") THEN
- LowerExpr(AST.Child(n, 1), et, q); t := SymTab.CharType()
- ELSIF SymTab.Equal(nm, "ORD") THEN
- LowerExpr(AST.Child(n, 1), et, q); t := SymTab.IntType()
- ELSIF SymTab.Equal(nm, "UCHR") THEN
- LowerExpr(AST.Child(n, 1), et, q); t := SymTab.UCharType()
- ELSIF SymTab.Equal(nm, "CHR8") THEN
- LowerExpr(AST.Child(n, 1), et, q);
- QbeGen.WidenLong(q, qa); QbeGen.CheckRange(qa, "0", "255");
- t := SymTab.CharType()
- ELSIF SymTab.Equal(nm, "UORD") THEN
- LowerExpr(AST.Child(n, 1), et, q); t := SymTab.IntType()
- ELSIF SymTab.Equal(nm, "SIZE") OR SymTab.Equal(nm, "TSIZE") THEN
- LowerDesign(AST.Child(n, 1), dt, k, qd, qn, sfx);
- QbeGen.IntStr(VAL(INTEGER, SymTab.ObjectSize(dt)), q);
- t := SymTab.IntType()
- ELSIF SymTab.Equal(nm, "MIN") OR SymTab.Equal(nm, "MAX") THEN
- isMax := SymTab.Equal(nm, "MAX");
- LowerDesign(AST.Child(n, 1), dt, k, qd, qn, sfx);
- IF SymTab.ClassOf(dt) = SymTab.ClReal THEN
- IF isMax THEN QbeGen.NormReal("3.402823e38", q)
- ELSE QbeGen.NormReal("-3.402823e38", q) END;
- t := SymTab.RealType()
- ELSIF SymTab.ClassOf(dt) = SymTab.ClLong THEN
- IF isMax THEN QbeGen.CopyOp("9223372036854775807", q)
- ELSE QbeGen.CopyOp("-9223372036854775808", q) END;
- t := SymTab.LongType()
- ELSIF SymTab.TypeBounds(dt, lo, hi) THEN
- IF isMax THEN QbeGen.IntStr(hi, q) ELSE QbeGen.IntStr(lo, q) END;
- t := SymTab.IntType()
- ELSE t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
- END
- ELSIF SymTab.Equal(nm, "LEN") OR SymTab.Equal(nm, "HIGH") THEN
- IF AST.Kind(AST.Child(n, 1)) = AST.NkStrLit THEN
- (* LEN("literal"): the content length of the literal; a real
- string literal is still declared, as the grammar does *)
- Name(AST.Child(n, 1), qn);
- IF (qn[0] = "U") OR (qn[0] = "u") THEN
- QbeGen.DeclUStr(qn, qd, isU, ucp, uok);
- IF NOT uok THEN t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
- ELSIF isU THEN
- IF SymTab.Equal(nm, "HIGH") THEN QbeGen.CopyOp("0", q)
- ELSE QbeGen.CopyOp("1", q)
- END;
- t := SymTab.IntType()
- ELSE
- QbeGen.UStrLen(qd, qr);
- IF SymTab.Equal(nm, "HIGH") THEN QbeGen.DecQ(qr) END;
- QbeGen.CopyOp(qr, q); t := SymTab.IntType()
- END
- ELSE
- IF SymTab.StrLen(qn) = 3 THEN c1 := 1
- ELSE QbeGen.DeclStr(qn, qd); c1 := SymTab.StrLen(qn) - 2
- END;
- IF SymTab.Equal(nm, "HIGH") THEN DEC(c1) END;
- QbeGen.IntStr(c1, q); t := SymTab.IntType()
- END
- ELSE
- LowerDesign(AST.Child(n, 1), dt, k, qd, qn, sfx);
- IF dt = SymTab.InvalidType THEN
- t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
- ELSIF SymTab.ClassOf(dt) = SymTab.ClArray THEN
- IF SymTab.Equal(nm, "HIGH") THEN
- IF SymTab.IsOpenArray(dt) THEN QbeGen.OpenHi(qd, q)
- ELSE QbeGen.IntStr(SymTab.ArrayHi(dt), q) END
- ELSE
- IF SymTab.IsOpenArray(dt) THEN QbeGen.LoadCount(qd, q)
- ELSE QbeGen.IntStr(VAL(INTEGER, SymTab.ArrayLen(dt)), q) END
- END;
- t := SymTab.IntType()
- ELSE
- t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
- END
- END;
- ELSIF SymTab.Equal(nm, "VAL") THEN
- Name(AST.Child(n, 1), vn);
- IF NOT SymTab.Lookup(vn) THEN
- t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
- ELSE
- vt := SymTab.SymType(vn);
- LowerExpr(AST.Child(n, 2), et, q);
- c1 := SymTab.ClassOf(et); c2 := SymTab.ClassOf(vt);
- IF (c1 = SymTab.ClInt) AND (c2 = SymTab.ClLong) THEN
- QbeGen.WidenLong(q, qa); QbeGen.CopyOp(qa, q); t := vt
- ELSIF (c1 = SymTab.ClLong) AND (c2 = SymTab.ClInt) THEN
- QbeGen.NarrowLong(q, qa); QbeGen.CopyOp(qa, q); t := vt
- ELSIF (c1 = SymTab.ClInt) AND (c2 = SymTab.ClReal) THEN
- QbeGen.ConvIR(q, qa); QbeGen.CopyOp(qa, q); t := vt
- ELSIF (c1 = SymTab.ClLong) AND (c2 = SymTab.ClReal) THEN
- QbeGen.ConvLR(q, qa); QbeGen.CopyOp(qa, q); t := vt
- ELSIF (c1 = SymTab.ClReal) AND (c2 = SymTab.ClInt) THEN
- QbeGen.ConvRI(q, qa); QbeGen.CopyOp(qa, q); t := vt
- ELSIF (c1 = SymTab.ClReal) AND (c2 = SymTab.ClLong) THEN
- QbeGen.ConvRL(q, qa); QbeGen.CopyOp(qa, q); t := vt
- ELSIF (c1 = SymTab.ClSet) AND (c2 = SymTab.ClInt) THEN
- (* a set as its (first) machine word *)
- QbeGen.LoadW(q, qa); QbeGen.CopyOp(qa, q); t := vt
- ELSIF (c1 = SymTab.ClInt) AND (c2 = SymTab.ClSet) THEN
- (* a word as a set value: zero-filled one-word temp *)
- c2 := SymTab.SetWords(vt);
- IF c2 = 0 THEN c2 := 1 END;
- QbeGen.NewSetTemp(VAL(CARDINAL, c2), qa);
- QbeGen.SetZero(qa, VAL(CARDINAL, c2));
- QbeGen.StoreW(qa, q); QbeGen.CopyOp(qa, q); t := vt
- ELSE t := vt (* same representation: no-op *)
- END
- END
- ELSIF SymTab.Equal(nm, "SHIFT") OR SymTab.Equal(nm, "ROTATE") THEN
- LowerExpr(AST.Child(n, 1), et, q);
- LowerExpr(AST.Child(n, 2), et2, q2);
- QbeGen.SetShift(q, q2, SymTab.SetWords(et), SymTab.SetCount(et),
- SymTab.Equal(nm, "ROTATE"), qt);
- t := et; QbeGen.CopyOp(qt, q)
- ELSIF SymTab.Equal(nm, "ADR") THEN
- LowerDesign(AST.Child(n, 1), dt, k, qd, qn, sfx);
- IF k = SymTab.KindProc THEN
- (* ADR of a procedure is its code address *)
- LowerProcAddr(qn, q)
- ELSIF sfx THEN QbeGen.CopyOp(qd, q)
- ELSIF (k = SymTab.KindVar) OR (k = SymTab.KindParam) THEN
- QbeGen.AddrOf(qn, q)
- ELSE QbeGen.CopyOp("0", q)
- END;
- t := SymTab.AddrType()
- ELSE t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
- END
- END LowerBuiltinExpr;
- PROCEDURE LowerBuiltinStmt (n: AST.Node; nm: SymTab.Name);
- (* Statement builtins (AssOrCall): INC/DEC, INCL/EXCL, NEW/DISPOSE. *)
- VAR dt, at, et2, bt: SymTab.TypeIndex; dk: INTEGER;
- qd, qv, qe, qn2, qstep, qm: QbeGen.QVal;
- qn: SymTab.Name; sfx, isInc: BOOLEAN;
- BEGIN
- IF SymTab.Equal(nm, "INC") OR SymTab.Equal(nm, "DEC") THEN
- isInc := SymTab.Equal(nm, "INC");
- QbeGen.CopyOp("1", qstep);
- LowerDesign(AST.Child(n, 1), dt, dk, qd, qn, sfx);
- IF AST.NChild(n) > 2 THEN LowerExpr(AST.Child(n, 2), et2, qstep) END;
- IF dt = SymTab.InvalidType THEN
- ELSE
- IF sfx OR (dk = SymTab.KindField) THEN QbeGen.ElemLoad(qd, dt, qv)
- ELSE QbeGen.LoadVar(qn, FALSE, qv)
- END;
- QbeGen.NewTemp(qn2);
- IF isInc THEN QbeGen.Op3("add", qn2, qv, qstep, FALSE)
- ELSE QbeGen.Op3("sub", qn2, qv, qstep, FALSE)
- END;
- IF sfx OR (dk = SymTab.KindField) THEN QbeGen.ElemStore(qd, qn2, dt)
- ELSE QbeGen.StoreVar(qn, qn2, FALSE)
- END
- END
- ELSIF SymTab.Equal(nm, "INCL") OR SymTab.Equal(nm, "EXCL") THEN
- isInc := SymTab.Equal(nm, "INCL");
- LowerDesign(AST.Child(n, 1), at, dk, qd, qn, sfx);
- LowerExpr(AST.Child(n, 2), et2, qe);
- IF at = SymTab.InvalidType THEN
- ELSIF SymTab.ClassOf(at) # SymTab.ClSet THEN
- ELSE
- IF isInc THEN QbeGen.SetBit(qd, qe, SymTab.SetBaseLo(at), SymTab.SetCount(at))
- ELSE QbeGen.SetClearBit(qd, qe, SymTab.SetBaseLo(at), SymTab.SetCount(at))
- END
- END
- ELSIF SymTab.Equal(nm, "NEW") THEN
- LowerDesign(AST.Child(n, 1), dt, dk, qd, qn, sfx);
- IF dt = SymTab.InvalidType THEN
- ELSIF SymTab.ClassOf(dt) # SymTab.ClPtr THEN
- ELSE
- bt := SymTab.PtrBase(dt);
- IF bt # SymTab.InvalidType THEN
- QbeGen.NewHeap(bt, qm); QbeGen.InitHeap(qm, bt);
- IF sfx OR (dk = SymTab.KindField) THEN QbeGen.ElemStore(qd, qm, dt)
- ELSE QbeGen.StorePtr(qn, qm)
- END
- END
- END
- ELSIF SymTab.Equal(nm, "DISPOSE") THEN
- LowerDesign(AST.Child(n, 1), dt, dk, qd, qn, sfx);
- IF dt = SymTab.InvalidType THEN
- ELSIF SymTab.ClassOf(dt) # SymTab.ClPtr THEN
- ELSE
- IF sfx OR (dk = SymTab.KindField) THEN QbeGen.ElemLoad(qd, dt, qv)
- ELSE QbeGen.LoadPtr(qn, qv)
- END;
- QbeGen.FreeHeap(qv);
- IF sfx OR (dk = SymTab.KindField) THEN QbeGen.ElemStore(qd, "0", dt)
- ELSE QbeGen.StorePtr(qn, "0")
- END
- END
- END
- END LowerBuiltinStmt;
- PROCEDURE LowerMethod (m: AST.Node; ct: SymTab.TypeIndex);
- (* A class method: hidden THIS receiver, then declared formals; the body
- runs with THIS pushed as the WITH base (bare field names resolve). *)
- VAR nm, txt: SymTab.Name; mg, thisQ: QbeGen.QVal;
- uid, depth, np: CARDINAL; res, pt: SymTab.TypeIndex;
- pv, ext: BOOLEAN; j: CARDINAL; saveCls: SymTab.TypeIndex;
- savedProc: INTEGER;
- BEGIN
- IF (AST.Op(m) # 0) AND (AST.Op(m) # 3) THEN RETURN END;
- Name(AST.Child(m, 0), nm);
- IF NOT ProcInfo(nm, uid, res, depth, ext, np) THEN RETURN END;
- QbeGen.Mangled(nm, SymTab.ClassMethodUid(ct, nm), mg);
- QbeGen.BeginFunc(mg);
- IF NOT QbeGen.FuncParam("THIS", TRUE, ct) THEN END;
- IF CompositeRes(res) THEN
- IF NOT QbeGen.FuncParam("__res", TRUE, res) THEN END
- END;
- j := 0;
- WHILE j < np DO
- IF NOT ParamInfo(nm, j, pt, pv, txt) THEN END;
- IF NOT QbeGen.FuncParam(txt, pv OR SymTab.IsOpenArray(pt), pt) THEN END;
- INC(j)
- END;
- QbeGen.SetFuncRes(res);
- QbeGen.EndFuncHeader;
- QbeGen.ThisBase(thisQ);
- saveCls := curImplCls;
- curImplCls := ct;
- QbeGen.PushWith(thisQ);
- IF withTop <= HIGH(withRec) THEN withRec[withTop] := ct; INC(withTop) END;
- savedProc := emitProc;
- emitProc := FindProc(nm);
- LowerDecls(AST.Child(m, 2));
- nUserLab := 0;
- LowerStmt(AST.Child(m, 1));
- emitProc := savedProc;
- IF withTop > 0 THEN DEC(withTop) END;
- curImplCls := saveCls;
- QbeGen.PopWith;
- QbeGen.EndFunc(res)
- END LowerMethod;
- PROCEDURE LowerMethods (seq: AST.Node; ct: SymTab.TypeIndex);
- VAR i: CARDINAL; m: AST.Node;
- BEGIN
- IF seq = AST.NoNode THEN RETURN END;
- i := 0;
- WHILE i < AST.NChild(seq) DO
- m := AST.Child(seq, i);
- IF i = AST.MaxChild - 1 THEN LowerMethods(m, ct)
- ELSIF AST.Kind(m) = AST.NkFieldDecl THEN LowerMethods(m, ct)
- ELSIF AST.Kind(m) = AST.NkProcDecl THEN LowerMethod(m, ct)
- END;
- INC(i)
- END
- END LowerMethods;
- PROCEDURE LowerMethodHeadings (seq: AST.Node; ct: SymTab.TypeIndex);
- (* Class-declaration method headings: emit+discard a function header per
- method so the backend temp counters advance exactly as the legacy
- parse did (the implementation's temps then line up). *)
- VAR i, j: CARDINAL; m: AST.Node; nm, txt: SymTab.Name;
- mg: QbeGen.QVal; uid, depth, np: CARDINAL; res, pt: SymTab.TypeIndex;
- pv, ext: BOOLEAN;
- BEGIN
- IF seq = AST.NoNode THEN RETURN END;
- i := 0;
- WHILE i < AST.NChild(seq) DO
- m := AST.Child(seq, i);
- IF i = AST.MaxChild - 1 THEN LowerMethodHeadings(m, ct)
- ELSIF AST.Kind(m) = AST.NkFieldDecl THEN LowerMethodHeadings(m, ct)
- ELSIF AST.Kind(m) = AST.NkProcDecl THEN
- Name(AST.Child(m, 0), nm);
- IF ProcInfo(nm, uid, res, depth, ext, np) THEN
- QbeGen.Mangled(nm, SymTab.ClassMethodUid(ct, nm), mg);
- QbeGen.BeginFunc(mg);
- j := 0;
- WHILE j < np DO
- IF NOT ParamInfo(nm, j, pt, pv, txt) THEN END;
- IF NOT QbeGen.FuncParam(txt, pv OR SymTab.IsOpenArray(pt), pt) THEN END;
- INC(j)
- END;
- QbeGen.SetFuncRes(res);
- QbeGen.AbortFunc
- END
- END;
- INC(i)
- END
- END LowerMethodHeadings;
- PROCEDURE LowerMethodCall (n: AST.Node; VAR t: SymTab.TypeIndex;
- VAR q: QbeGen.QVal; want: BOOLEAN);
- VAR d: AST.Node; ct, res, ft, at: SymTab.TypeIndex;
- cq, mg, qa, qt, qe, qres: QbeGen.QVal; fn, pn: SymTab.Name;
- k, vs: INTEGER; i: CARDINAL; sfx, isV, conv: BOOLEAN;
- BEGIN
- d := AST.Child(n, 0);
- curMethCls := SymTab.InvalidType;
- LowerDesign(d, ct, k, cq, pn, sfx);
- ct := curMethCls;
- IF ct = SymTab.InvalidType THEN
- t := SymTab.InvalidType; QbeGen.CopyOp("0", q); RETURN
- END;
- IF AST.NChild(d) = 1 THEN
- Name(AST.Child(d, 0), fn)
- ELSE
- Name(AST.Child(AST.Child(d, AST.NChild(d) - 1), 0), fn)
- END;
- res := SymTab.ClassMethodRes(ct, fn);
- vs := SymTab.VirtSlot(ct, fn);
- IF vs >= 0 THEN QbeGen.VirtCallBegin(cq, vs, res)
- ELSE
- QbeGen.Mangled(fn, SymTab.ClassMethodUid(ct, fn), mg);
- QbeGen.CallBegin(mg, res, 0, FALSE)
- END;
- IF CompositeRes(res) THEN
- QbeGen.AllocLocal(res, qres);
- IF NOT QbeGen.CallArg(qres, "l") THEN END
- END;
- i := 1;
- WHILE i < AST.NChild(n) DO
- LowerExpr(AST.Child(n, i), at, qe);
- ft := SymTab.ClassMethodParamType(ct, fn, i - 1);
- isV := SymTab.ClassMethodParamIsVar(ct, fn, i - 1);
- IF isV THEN
- IF QbeGen.AddrOfVal(qe, qa) THEN
- IF NOT QbeGen.CallArg(qa, "l") THEN END
- END
- ELSE
- conv := (SymTab.ClassOf(ft) = SymTab.ClReal) AND SymTab.IsIntFamily(at);
- IF conv THEN
- QbeGen.ConvIR(qe, qt);
- IF NOT QbeGen.CallArg(qt, "d") THEN END
- ELSE
- IF NOT QbeGen.CallArg(qe, QbeGen.ArgClass(ft)) THEN END
- END
- END;
- INC(i)
- END;
- IF want THEN t := res; QbeGen.CallEnd(TRUE, q);
- IF CompositeRes(res) THEN QbeGen.CopyOp(qres, q) END
- ELSE t := SymTab.InvalidType; QbeGen.CopyOp("0", q); QbeGen.CallEnd(FALSE, q)
- END
- END LowerMethodCall;
- PROCEDURE LowerCase (n: AST.Node);
- (* CASE as a compare-chain (mirrors CaseStat/CaseAlt/CaseLabel). The
- arm list is chunked (AstAppend) so a CASE with more than MaxChild
- alternatives is not truncated. *)
- VAR i, j: CARDINAL; arm, lab, seq, next: AST.Node;
- tsel, t2, t3: SymTab.TypeIndex;
- qsel, q2, q3, qc, qd, qe, lEnd, lBody, lAfter, lNext: QbeGen.QVal;
- BEGIN
- LowerExpr(AST.Child(n, 0), tsel, qsel);
- QbeGen.NewLabel(lEnd);
- (* pass 1: alternatives (ELSE arms come after) *)
- seq := AST.Child(n, 1);
- WHILE seq # AST.NoNode DO
- i := 0;
- WHILE i < AST.NChild(seq) DO
- arm := AST.Child(seq, i);
- IF AST.Kind(arm) = AST.NkBlock THEN
- (* continuation chunk *)
- ELSIF AST.Op(arm) = 1 THEN
- (* ELSE arm: emitted in pass 2 *)
- ELSE
- QbeGen.NewLabel(lBody); QbeGen.NewLabel(lAfter);
- j := 0;
- WHILE j + 1 < AST.NChild(arm) DO
- lab := AST.Child(arm, j);
- IF AST.Kind(lab) = AST.NkSubrange THEN
- LowerExpr(AST.Child(lab, 0), t2, q2);
- QbeGen.NewLabel(lNext);
- QbeGen.Cmp(SymTab.OpEq, qsel, q2, qc, FALSE);
- QbeGen.Jnz(qc, lBody, lNext);
- QbeGen.EmitLabel(lNext);
- LowerExpr(AST.Child(lab, 1), t3, q3);
- QbeGen.Cmp(SymTab.OpGe, qsel, q2, qc, FALSE);
- QbeGen.Cmp(SymTab.OpLe, qsel, q3, qd, FALSE);
- QbeGen.NewTemp(qe);
- QbeGen.Op3("and", qe, qc, qd, FALSE);
- QbeGen.NewLabel(lNext);
- QbeGen.Jnz(qe, lBody, lNext);
- QbeGen.EmitLabel(lNext)
- ELSE
- LowerExpr(lab, t2, q2);
- QbeGen.NewLabel(lNext);
- QbeGen.Cmp(SymTab.OpEq, qsel, q2, qc, FALSE);
- QbeGen.Jnz(qc, lBody, lNext);
- QbeGen.EmitLabel(lNext)
- END;
- INC(j)
- END;
- QbeGen.Jmp(lAfter);
- QbeGen.EmitLabel(lBody);
- LowerStmt(AST.Child(arm, AST.NChild(arm) - 1));
- QbeGen.Jmp(lEnd);
- QbeGen.EmitLabel(lAfter)
- END;
- INC(i)
- END;
- next := AST.NoNode;
- IF AST.NChild(seq) > 0 THEN
- next := AST.Child(seq, AST.NChild(seq) - 1);
- IF AST.Kind(next) # AST.NkBlock THEN next := AST.NoNode END
- END;
- seq := next
- END;
- (* pass 2: ELSE arms *)
- seq := AST.Child(n, 1);
- WHILE seq # AST.NoNode DO
- i := 0;
- WHILE i < AST.NChild(seq) DO
- arm := AST.Child(seq, i);
- IF (AST.Kind(arm) # AST.NkBlock) AND (AST.Op(arm) = 1) THEN
- LowerStmt(AST.Child(arm, 0))
- END;
- INC(i)
- END;
- next := AST.NoNode;
- IF AST.NChild(seq) > 0 THEN
- next := AST.Child(seq, AST.NChild(seq) - 1);
- IF AST.Kind(next) # AST.NkBlock THEN next := AST.NoNode END
- END;
- seq := next
- END;
- QbeGen.EmitLabel(lEnd)
- END LowerCase;
- PROCEDURE LowerWith (n: AST.Node);
- (* WITH: push each record's base address, lower the body, pop only the
- levels this WITH pushed (a nested WITH must restore the outer one). *)
- VAR i, base: CARDINAL; dt: SymTab.TypeIndex; dk: INTEGER;
- qd: QbeGen.QVal; qn: SymTab.Name; sfx: BOOLEAN;
- BEGIN
- base := withTop;
- i := 0;
- WHILE i + 1 < AST.NChild(n) DO
- LowerDesign(AST.Child(n, i), dt, dk, qd, qn, sfx);
- QbeGen.PushWith(qd);
- IF withTop <= HIGH(withRec) THEN
- withRec[withTop] := dt; INC(withTop)
- END;
- INC(i)
- END;
- LowerStmt(AST.Child(n, AST.NChild(n) - 1));
- WHILE withTop > base DO DEC(withTop); QbeGen.PopWith END
- END LowerWith;
- PROCEDURE LowerProcAddr (nm: ARRAY OF CHAR; VAR q: QbeGen.QVal);
- (* Code address of a procedure used as a value (assign to a procedure
- variable): $<base>_<uid>, or the plain C link for external procs. *)
- VAR uid, depth, np: CARDINAL; res: SymTab.TypeIndex; ext: BOOLEAN;
- mg: QbeGen.QVal;
- base: SymTab.Name; ok: BOOLEAN;
- BEGIN
- ok := ProcInfo(nm, uid, res, depth, ext, np);
- IF NOT ok THEN
- (* a qualified/imported procedure appears under its base symbol
- name in the proc table *)
- IF SymTab.SymBase(nm, base) THEN
- ok := ProcInfo(base, uid, res, depth, ext, np)
- END
- END;
- IF ok THEN
- IF ext THEN QbeGen.CopyOp(nm, mg) ELSE QbeGen.Mangled(nm, uid, mg) END;
- QbeGen.ProcAddr(mg, q)
- ELSE QbeGen.CopyOp("0", q)
- END
- END LowerProcAddr;
- PROCEDURE LowerExprFull (n: AST.Node; VAR t: SymTab.TypeIndex;
- VAR q: QbeGen.QVal; VAR k: INTEGER;
- VAR qn: SymTab.Name; VAR sfx: BOOLEAN;
- asAddr: BOOLEAN);
- VAR t2: SymTab.TypeIndex; q2, qt, qf, qs, qr, qaddr, qa, qb: QbeGen.QVal;
- lNext, lFalse, lTrue, lDone: QbeGen.QVal;
- kind, op, rop, ucp: INTEGER;
- folded, isR, isCh, isU, uok: BOOLEAN;
- i: CARDINAL; el, seq, next: AST.Node;
- lw, rw, mw: CARDINAL; idx: INTEGER; fok, isL: BOOLEAN;
- lt, rt: SymTab.TypeIndex; qw: QbeGen.QVal;
- BEGIN
- IF AST.Kind(n) = AST.NkDesignator THEN
- LowerDesign(n, t, k, q, qn, sfx);
- IF k = SymTab.KindProc THEN
- (* bare procedure used as a value: its code address *)
- LowerProcAddr(qn, q);
- RETURN
- END;
- (* Record value->address for VAR actuals, mirroring the grammar's
- Design. AddrOfVal cannot rely on the grammar's notes: its ring
- (256) wraps long before a whole-session emit finishes. *)
- IF sfx THEN QbeGen.CopyOp(q, qaddr) ELSE QbeGen.AddrOf(qn, qaddr) END;
- IF NOT asAddr THEN FinalLoad(t, sfx, q) END;
- IF (k = SymTab.KindVar) OR (k = SymTab.KindParam)
- OR (k = SymTab.KindField) THEN
- QbeGen.NoteAddr(q, qaddr)
- ELSIF sfx AND (t # SymTab.InvalidType)
- AND ((SymTab.ClassOf(t) = SymTab.ClArray)
- OR (SymTab.ClassOf(t) = SymTab.ClSet)
- OR (SymTab.ClassOf(t) = SymTab.ClRecord)) THEN
- QbeGen.NoteAddr(q, qaddr)
- END;
- RETURN
- END;
- k := -1; qn[0] := CHR(0); sfx := FALSE;
- IF AST.Kind(n) = AST.NkIntLit THEN
- Name(n, qn); QbeGen.NormInt(qn, q); t := SymTab.IntType()
- ELSIF AST.Kind(n) = AST.NkCharLit THEN
- Name(n, qn); QbeGen.NormLit(qn, q, isCh); t := SymTab.CharType()
- ELSIF AST.Kind(n) = AST.NkRealLit THEN
- Name(n, qn); QbeGen.NormReal(qn, q); t := SymTab.RealType()
- ELSIF AST.Kind(n) = AST.NkStrLit THEN
- Name(n, qn);
- IF (qn[0] = "U") OR (qn[0] = "u") THEN
- (* U"..." / U'...': one codepoint -> UCHAR, else a ustring *)
- QbeGen.DeclUStr(qn, q, isU, ucp, uok);
- IF NOT uok THEN
- t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
- ELSIF isU THEN
- t := SymTab.UCharType(); QbeGen.IntStr(ucp, q)
- ELSE
- t := AST.Ty(n); IF t = -1 THEN t := SymTab.NewUStr() END; QbeGen.NoteAddr(q, q)
- END
- ELSIF SymTab.StrLen(qn) = 3 THEN
- t := SymTab.CharType(); QbeGen.IntStr(QbeGen.CharVal(qn), q)
- ELSE
- t := AST.Ty(n); IF t = -1 THEN t := SymTab.NewStr() END; QbeGen.DeclStr(qn, q); QbeGen.NoteAddr(q, q)
- END
- ELSIF AST.Kind(n) = AST.NkSetLit THEN
- t := AST.Ty(n); IF t = -1 THEN t := SymTab.NewSet(SymTab.NewSubR(0, 255)) END;
- QbeGen.NewSetTemp(8, q); QbeGen.SetZero(q, 8);
- i := 0;
- WHILE i < AST.NChild(n) DO
- el := AST.Child(n, i);
- IF AST.Kind(el) = AST.NkSubrange THEN
- LowerExpr(AST.Child(el, 0), t2, q2);
- LowerExpr(AST.Child(el, 1), t2, qt);
- QbeGen.SetRange(q, q2, qt, SymTab.SetBaseLo(t), SymTab.SetCount(t))
- ELSE
- LowerExpr(el, t2, q2);
- QbeGen.SetBit(q, q2, SymTab.SetBaseLo(t), SymTab.SetCount(t))
- END;
- INC(i)
- END
- ELSIF AST.Kind(n) = AST.NkBraceLit THEN
- (* T{...} typed brace literal: a set here (the type was recorded by
- the grammar in the node's ty field). Array/record/class
- constructors are not lowered here yet. *)
- t := AST.Ty(n);
- IF (t # SymTab.InvalidType) AND (SymTab.ClassOf(t) = SymTab.ClSet) THEN
- IF SymTab.SetWords(t) = 0 THEN
- QbeGen.NewSetTemp(8, q); QbeGen.SetZero(q, 8)
- ELSE
- QbeGen.NewSetTemp(SymTab.SetWords(t), q);
- QbeGen.SetZero(q, SymTab.SetWords(t))
- END;
- seq := n;
- WHILE seq # AST.NoNode DO
- i := 0;
- WHILE i < AST.NChild(seq) DO
- el := AST.Child(seq, i);
- IF AST.Kind(el) # AST.NkBlock THEN
- IF AST.Kind(el) = AST.NkSubrange THEN
- LowerExpr(AST.Child(el, 0), t2, q2);
- LowerExpr(AST.Child(el, 1), t2, qt);
- QbeGen.SetRange(q, q2, qt, SymTab.SetBaseLo(t),
- SymTab.SetCount(t))
- ELSE
- LowerExpr(el, t2, q2);
- QbeGen.SetBit(q, q2, SymTab.SetBaseLo(t), SymTab.SetCount(t))
- END
- END;
- INC(i)
- END;
- next := AST.NoNode;
- IF AST.NChild(seq) > 0 THEN
- next := AST.Child(seq, AST.NChild(seq) - 1);
- IF AST.Kind(next) # AST.NkBlock THEN next := AST.NoNode END
- END;
- seq := next
- END
- ELSIF (t # SymTab.InvalidType)
- AND ((SymTab.ClassOf(t) = SymTab.ClArray)
- OR (SymTab.ClassOf(t) = SymTab.ClRecord)
- OR (SymTab.ClassOf(t) = SymTab.ClClass)) THEN
- (* T{...} aggregate constructor *)
- QbeGen.CtorBegin(t);
- seq := n;
- WHILE seq # AST.NoNode DO
- i := 0;
- WHILE i < AST.NChild(seq) DO
- el := AST.Child(seq, i);
- IF AST.Kind(el) # AST.NkBlock THEN
- LowerExpr(el, t2, q2);
- QbeGen.CtorElem(q2)
- END;
- INC(i)
- END;
- next := AST.NoNode;
- IF AST.NChild(seq) > 0 THEN
- next := AST.Child(seq, AST.NChild(seq) - 1);
- IF AST.Kind(next) # AST.NkBlock THEN next := AST.NoNode END
- END;
- seq := next
- END;
- QbeGen.CtorEnd(q)
- ELSE
- t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
- END
- ELSIF AST.Kind(n) = AST.NkDesignator THEN
- LowerDesign(n, t, k, q, qn, sfx)
- ELSIF AST.Kind(n) = AST.NkCall THEN
- IF IsMethodCall(n) THEN LowerMethodCall(n, t, q, TRUE)
- ELSE
- CalleeName(n, qn);
- IF IsBuiltin(qn) THEN LowerBuiltinExpr(n, qn, t, q)
- ELSE LowerCall(n, t, q, TRUE)
- END
- END
- ELSIF AST.Kind(n) = AST.NkUnary THEN
- LowerExpr(AST.Child(n, 0), t, q);
- IF AST.Op(n) = AST.OpSub THEN
- IF QbeGen.IsImm(q) THEN QbeGen.NegFold(q, q)
- ELSE QbeGen.NewTemp(qt);
- QbeGen.NegQ(q, qt, SymTab.ClassOf(t) = SymTab.ClReal);
- QbeGen.CopyOp(qt, q)
- END
- ELSE
- QbeGen.NotQ(q, qt); QbeGen.CopyOp(qt, q); t := SymTab.BoolType()
- END
- ELSIF AST.Kind(n) = AST.NkBinExpr THEN
- op := AST.Op(n);
- IF IsRel(op) THEN
- LowerExpr(AST.Child(n, 0), t, q);
- LowerExpr(AST.Child(n, 1), t2, q2);
- rop := SymTab.OpEq; folded := RelSym(op, rop);
- IF op = AST.OpIn THEN
- QbeGen.InSet(q, q2, SymTab.SetBaseLo(t2), SymTab.SetCount(t2), qt);
- t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- ELSIF (SymTab.ClassOf(t) = SymTab.ClPtr)
- OR (SymTab.ClassOf(t2) = SymTab.ClPtr) THEN
- QbeGen.CmpL(rop, q, q2, qt);
- t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- ELSIF (SymTab.ClassOf(t) = SymTab.ClSet)
- OR (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
- QbeGen.CmpSet(rop, q, q2, SymTab.SetWords(t), SymTab.SetWords(t2), qt);
- t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- ELSIF SymTab.StrCompat(t, t2) THEN
- QbeGen.StrEq(rop, q, q2, qt);
- t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- ELSIF SymTab.IsLongFamily(t) OR SymTab.IsLongFamily(t2) THEN
- IF SymTab.IsIntFamily(t) THEN QbeGen.WidenLong(q, qf); QbeGen.CopyOp(qf, q) END;
- IF SymTab.IsIntFamily(t2) THEN QbeGen.WidenLong(q2, qf); QbeGen.CopyOp(qf, q2) END;
- QbeGen.CmpLong(rop, q, q2, qt);
- t := SymTab.BoolType(); QbeGen.CopyOp(qt, q)
- ELSE
- isR := (t # SymTab.InvalidType) AND (SymTab.ClassOf(t) = SymTab.ClReal);
- t := SymTab.BoolType();
- QbeGen.Cmp(rop, q, q2, qt, isR);
- QbeGen.CopyOp(qt, q)
- END
- ELSIF op = AST.OpAnd THEN
- LowerExpr(AST.Child(n, 0), t, q);
- QbeGen.DelayBegin;
- LowerExpr(AST.Child(n, 1), t2, q2);
- QbeGen.DelayEnd;
- QbeGen.Slot4(qs);
- QbeGen.NewLabel(lNext); QbeGen.NewLabel(lFalse); QbeGen.NewLabel(lDone);
- QbeGen.Jnz(q, lNext, lFalse);
- QbeGen.EmitLabel(lNext);
- QbeGen.DelayFlush;
- QbeGen.StoreW(qs, q2);
- QbeGen.Jmp(lDone);
- QbeGen.EmitLabel(lFalse);
- QbeGen.StoreW(qs, "0");
- QbeGen.Jmp(lDone);
- QbeGen.EmitLabel(lDone);
- QbeGen.LoadW(qs, qr);
- QbeGen.CopyOp(qr, q);
- t := SymTab.BoolType()
- ELSIF op = AST.OpOr THEN
- LowerExpr(AST.Child(n, 0), t, q);
- QbeGen.DelayBegin;
- LowerExpr(AST.Child(n, 1), t2, q2);
- QbeGen.DelayEnd;
- QbeGen.Slot4(qs);
- QbeGen.NewLabel(lTrue); QbeGen.NewLabel(lNext); QbeGen.NewLabel(lDone);
- QbeGen.Jnz(q, lTrue, lNext);
- QbeGen.EmitLabel(lTrue);
- QbeGen.StoreW(qs, "1");
- QbeGen.Jmp(lDone);
- QbeGen.EmitLabel(lNext);
- QbeGen.DelayFlush;
- QbeGen.StoreW(qs, q2);
- QbeGen.Jmp(lDone);
- QbeGen.EmitLabel(lDone);
- QbeGen.LoadW(qs, qr);
- QbeGen.CopyOp(qr, q);
- t := SymTab.BoolType()
- ELSE
- LowerExpr(AST.Child(n, 0), t, q);
- LowerExpr(AST.Child(n, 1), t2, q2);
- IF (t # SymTab.InvalidType) AND (t2 # SymTab.InvalidType)
- AND (SymTab.ClassOf(t) = SymTab.ClSet)
- AND (SymTab.ClassOf(t2) = SymTab.ClSet) THEN
- (* set union/diff (Add/Sub) or intersection/symdiff (Mul/Div) *)
- lw := SymTab.SetWords(t); rw := SymTab.SetWords(t2);
- mw := lw; IF rw > mw THEN mw := rw END;
- IF op = AST.OpAdd THEN idx := 0
- ELSIF op = AST.OpSub THEN idx := 2
- ELSIF op = AST.OpMul THEN idx := 1
- ELSE idx := 3
- END;
- QbeGen.SetBinOp(idx, q, q2, lw, rw, qt);
- t := SymTab.NewSet(SymTab.NewSubR(0, VAL(INTEGER, mw) * 32 - 1));
- QbeGen.CopyOp(qt, q)
- ELSIF (op = AST.OpAdd)
- AND (SymTab.StrCompat(t, t2)
- OR (SymTab.IsStrType(t) AND (SymTab.ClassOf(t2) = SymTab.ClChar))
- OR ((SymTab.ClassOf(t) = SymTab.ClChar) AND SymTab.IsStrType(t2))) THEN
- QbeGen.StrFold(q, q2, SymTab.ClassOf(t), SymTab.ClassOf(t2), qt, fok);
- IF NOT fok THEN
- IF SymTab.StrCompat(t, t2) THEN QbeGen.StrCat(q, q2, qt)
- ELSIF SymTab.IsStrType(t) THEN
- QbeGen.DeclCharStr(q2, qs); QbeGen.StrCat(q, qs, qt)
- ELSE
- QbeGen.DeclCharStr(q, qs); QbeGen.StrCat(qs, q2, qt)
- END
- END;
- t := SymTab.NewStr(); QbeGen.CopyOp(qt, q)
- ELSIF (op = AST.OpAdd)
- AND (SymTab.UStrCompat(t, t2)
- OR ((SymTab.ClassOf(t) = SymTab.ClUChar)
- AND (SymTab.ClassOf(t2) = SymTab.ClUStr))
- OR ((SymTab.ClassOf(t) = SymTab.ClUStr)
- AND (SymTab.ClassOf(t2) = SymTab.ClUChar))
- OR ((SymTab.ClassOf(t) = SymTab.ClUChar)
- AND (SymTab.ClassOf(t2) = SymTab.ClUChar))) THEN
- (* UString concatenation: a UCHAR operand becomes a
- 1-codepoint UString; the result is a descriptor *)
- IF SymTab.ClassOf(t) = SymTab.ClUStr THEN QbeGen.CopyOp(q, qa)
- ELSE QbeGen.UStrFrom(q, qa)
- END;
- IF SymTab.ClassOf(t2) = SymTab.ClUStr THEN QbeGen.UStrCat(qa, q2, qt)
- ELSE QbeGen.UStrFrom(q2, qb); QbeGen.UStrCat(qa, qb, qt)
- END;
- t := SymTab.NewUStr(); QbeGen.CopyOp(qt, q)
- ELSE
- lt := t; rt := t2;
- IF (SymTab.ClassOf(lt) = SymTab.ClReal)
- OR (SymTab.ClassOf(rt) = SymTab.ClReal) THEN
- t := SymTab.RealType()
- ELSIF SymTab.IsLongFamily(lt) OR SymTab.IsLongFamily(rt) THEN
- t := SymTab.LongType()
- ELSE t := SymTab.IntType()
- END;
- isL := SymTab.IsLongFamily(t);
- isR := SymTab.ClassOf(t) = SymTab.ClReal;
- kind := 0;
- IF op = AST.OpSub THEN kind := 1
- ELSIF op = AST.OpMul THEN kind := 2
- ELSIF op = AST.OpDiv THEN kind := 3
- ELSIF op = AST.OpMod THEN kind := 4
- END;
- folded := FALSE;
- IF (NOT isL) AND (NOT isR) AND QbeGen.IsImm(q) AND QbeGen.IsImm(q2) THEN
- folded := QbeGen.Fold2(kind, q, q2, qf)
- END;
- IF folded THEN QbeGen.CopyOp(qf, q)
- ELSE
- IF isL THEN
- IF SymTab.IsIntFamily(lt) THEN
- QbeGen.WidenLong(q, qw); QbeGen.CopyOp(qw, q)
- END;
- IF SymTab.IsIntFamily(rt) THEN
- QbeGen.WidenLong(q2, qw); QbeGen.CopyOp(qw, q2)
- END;
- QbeGen.NewTemp(qt);
- IF op = AST.OpAdd THEN QbeGen.Op3L("add", qt, q, q2)
- ELSIF op = AST.OpSub THEN QbeGen.Op3L("sub", qt, q, q2)
- ELSIF op = AST.OpMul THEN QbeGen.Op3L("mul", qt, q, q2)
- ELSE QbeGen.Op3L("div", qt, q, q2)
- END
- ELSE
- QbeGen.NewTemp(qt);
- IF op = AST.OpAdd THEN QbeGen.Op3("add", qt, q, q2, isR)
- ELSIF op = AST.OpSub THEN QbeGen.Op3("sub", qt, q, q2, isR)
- ELSIF op = AST.OpMul THEN QbeGen.Op3("mul", qt, q, q2, isR)
- ELSIF op = AST.OpDiv THEN QbeGen.Op3("div", qt, q, q2, isR)
- ELSE QbeGen.Op3("rem", qt, q, q2, isR)
- END
- END;
- QbeGen.CopyOp(qt, q)
- END
- END
- END
- END
- END LowerExprFull;
- PROCEDURE LowerExpr (n: AST.Node; VAR t: SymTab.TypeIndex;
- VAR q: QbeGen.QVal);
- VAR k: INTEGER; qn: SymTab.Name; sfx: BOOLEAN;
- BEGIN
- LowerExprFull(n, t, q, k, qn, sfx, FALSE)
- END LowerExpr;
- PROCEDURE LowerStmt (seq: AST.Node);
- VAR i: CARDINAL; c, els, by: AST.Node; k: INTEGER;
- t, t2, t3, dt, et: SymTab.TypeIndex;
- q, q2, qt, qf, qd, qe: QbeGen.QVal;
- qlo, qhi, qk, qb, ql, qby: QbeGen.QVal; step: INTEGER;
- lTop, lBody, lEnd, lThen, lElse, lNext, lFalse, lDone, lx: QbeGen.QVal;
- qn, lv: SymTab.Name;
- kind, op, rop, cls: INTEGER;
- sfx, folded, isR, conv, wconv, hasElse: BOOLEAN;
- BEGIN
- IF seq = AST.NoNode THEN RETURN END;
- k := AST.Kind(seq);
- IF k = AST.NkBlock THEN
- i := 0;
- WHILE i < AST.NChild(seq) DO
- LowerStmt(AST.Child(seq, i)); INC(i)
- END
- ELSIF k = AST.NkAssign THEN
- LowerDesign(AST.Child(seq, 0), dt, cls, qd, qn, sfx);
- LowerExpr(AST.Child(seq, 1), et, qe);
- isR := (dt # SymTab.InvalidType) AND (SymTab.ClassOf(dt) = SymTab.ClReal);
- conv := isR AND SymTab.IsIntFamily(et);
- wconv := (dt # SymTab.InvalidType) AND SymTab.IsLongFamily(dt)
- AND SymTab.IsIntFamily(et);
- IF sfx OR (cls = SymTab.KindField) THEN
- IF SymTab.ClassOf(dt) = SymTab.ClArray THEN
- IF (et # SymTab.InvalidType) AND (SymTab.ClassOf(et) = SymTab.ClUStr)
- AND SymTab.IsUCharArray(dt) THEN QbeGen.UAssign(qd, qe)
- ELSIF SymTab.StrCompat(dt, et) THEN QbeGen.StrAssign(qd, qe)
- ELSE QbeGen.CopyArray(qd, qe, dt)
- END
- ELSIF SymTab.ClassOf(dt) = SymTab.ClSet THEN
- QbeGen.CopySet(qd, qe, SymTab.SetWords(dt), SymTab.SetWords(et))
- ELSIF SymTab.ClassOf(dt) = SymTab.ClRecord THEN
- QbeGen.CopyRecord(qd, qe, dt)
- ELSIF SymTab.IsLongFamily(dt) THEN
- IF wconv THEN QbeGen.WidenLong(qe, ql); QbeGen.ElemStore(qd, ql, dt)
- ELSE QbeGen.ElemStore(qd, qe, dt)
- END
- ELSIF conv THEN QbeGen.ConvIR(qe, qt); QbeGen.ElemStore(qd, qt, dt)
- ELSE QbeGen.ElemStore(qd, qe, dt)
- END
- ELSE
- IF SymTab.ClassOf(dt) = SymTab.ClArray THEN
- IF (et # SymTab.InvalidType) AND (SymTab.ClassOf(et) = SymTab.ClUStr)
- AND SymTab.IsUCharArray(dt) THEN QbeGen.UAssign(qd, qe)
- ELSIF SymTab.StrCompat(dt, et) THEN QbeGen.StrAssign(qd, qe)
- ELSE QbeGen.CopyArray(qd, qe, dt)
- END
- ELSIF SymTab.ClassOf(dt) = SymTab.ClSet THEN
- QbeGen.CopySet(qd, qe, SymTab.SetWords(dt), SymTab.SetWords(et))
- ELSIF SymTab.ClassOf(dt) = SymTab.ClRecord THEN
- QbeGen.CopyRecord(qd, qe, dt)
- ELSIF (SymTab.ClassOf(dt) = SymTab.ClPtr)
- OR (SymTab.ClassOf(dt) = SymTab.ClProc) THEN
- QbeGen.StorePtr(qn, qe)
- ELSIF SymTab.IsLongFamily(dt) THEN
- IF wconv THEN QbeGen.WidenLong(qe, ql); QbeGen.StoreLong(qn, ql)
- ELSE QbeGen.StoreLong(qn, qe)
- END
- ELSIF conv THEN QbeGen.ConvIR(qe, qt); QbeGen.StoreVar(qn, qt, TRUE)
- ELSE QbeGen.StoreVar(qn, qe, isR)
- END
- END
- ELSIF k = AST.NkIf THEN
- LowerExpr(AST.Child(seq, 0), t, q);
- QbeGen.NewLabel(lThen); QbeGen.NewLabel(lElse); QbeGen.NewLabel(lEnd);
- QbeGen.Jnz(q, lThen, lElse); QbeGen.EmitLabel(lThen);
- LowerStmt(AST.Child(seq, 1));
- QbeGen.Jmp(lEnd);
- els := AST.Child(seq, 2); hasElse := FALSE;
- WHILE (els # AST.NoNode) AND (AST.Kind(els) = AST.NkIf) DO
- QbeGen.EmitLabel(lElse); QbeGen.NewLabel(lElse);
- LowerExpr(AST.Child(els, 0), t, q);
- QbeGen.NewLabel(lThen); QbeGen.Jnz(q, lThen, lElse);
- QbeGen.EmitLabel(lThen);
- LowerStmt(AST.Child(els, 1));
- QbeGen.Jmp(lEnd);
- els := AST.Child(els, 2)
- END;
- IF els # AST.NoNode THEN
- QbeGen.EmitLabel(lElse); hasElse := TRUE; LowerStmt(els)
- END;
- IF hasElse THEN QbeGen.EmitLabel(lEnd)
- ELSE QbeGen.EmitLabel(lElse); QbeGen.EmitLabel(lEnd)
- END
- ELSIF k = AST.NkWhile THEN
- QbeGen.NewLabel(lTop); QbeGen.NewLabel(lBody); QbeGen.NewLabel(lEnd);
- QbeGen.EmitLabel(lTop);
- LowerExpr(AST.Child(seq, 0), t, q);
- QbeGen.Jnz(q, lBody, lEnd); QbeGen.EmitLabel(lBody);
- LowerStmt(AST.Child(seq, 1));
- QbeGen.Jmp(lTop); QbeGen.EmitLabel(lEnd)
- ELSIF k = AST.NkRepeat THEN
- QbeGen.NewLabel(lTop); QbeGen.NewLabel(lEnd); QbeGen.EmitLabel(lTop);
- LowerStmt(AST.Child(seq, 0));
- LowerExpr(AST.Child(seq, 1), t, q);
- QbeGen.Jnz(q, lEnd, lTop); QbeGen.EmitLabel(lEnd)
- ELSIF k = AST.NkLoop THEN
- QbeGen.NewLabel(lTop); QbeGen.NewLabel(lEnd);
- QbeGen.PushLoop(lEnd); QbeGen.EmitLabel(lTop);
- LowerStmt(AST.Child(seq, 0));
- QbeGen.Jmp(lTop); QbeGen.PopLoop; QbeGen.EmitLabel(lEnd)
- ELSIF k = AST.NkExit THEN
- IF QbeGen.TopLoop(lx) THEN QbeGen.Jmp(lx) END
- ELSIF k = AST.NkFor THEN
- Name(AST.Child(seq, 0), lv);
- LowerExpr(AST.Child(seq, 1), t, qlo);
- LowerExpr(AST.Child(seq, 2), t2, qhi);
- (* optional BY step: a constant, default +1; its sign also
- selects the termination comparison *)
- step := 1;
- IF AST.Child(seq, 3) # AST.NoNode THEN
- LowerExpr(AST.Child(seq, 3), t3, qby);
- IF NOT SymTab.ConstInt(qby, step) THEN step := 1
- ELSIF step = 0 THEN step := 1
- END
- END;
- QbeGen.StoreVar(lv, qlo, FALSE);
- QbeGen.NewLabel(lTop); QbeGen.NewLabel(lBody); QbeGen.NewLabel(lEnd);
- QbeGen.EmitLabel(lTop);
- QbeGen.LoadVar(lv, FALSE, qt);
- QbeGen.NewTemp(qk);
- IF step > 0 THEN QbeGen.Op3("cslew", qk, qt, qhi, FALSE)
- ELSE QbeGen.Op3("csgew", qk, qt, qhi, FALSE)
- END;
- QbeGen.Jnz(qk, lBody, lEnd);
- QbeGen.EmitLabel(lBody);
- LowerStmt(AST.Child(seq, 4));
- QbeGen.LoadVar(lv, FALSE, qt);
- QbeGen.IntStr(step, qb);
- QbeGen.NewTemp(qk);
- QbeGen.Op3("add", qk, qt, qb, FALSE);
- QbeGen.StoreVar(lv, qk, FALSE);
- QbeGen.Jmp(lTop); QbeGen.EmitLabel(lEnd)
- ELSIF k = AST.NkHalt THEN
- IF AST.Child(seq, 0) # AST.NoNode THEN
- LowerExpr(AST.Child(seq, 0), t, q) END;
- QbeGen.HaltQ
- ELSIF k = AST.NkGoto THEN
- Name(AST.Child(seq, 0), qn);
- UserLabel(qn, q);
- QbeGen.Jmp(q)
- ELSIF k = AST.NkLabel THEN
- Name(AST.Child(seq, 0), qn);
- UserLabel(qn, q);
- QbeGen.EmitLabel(q)
- ELSIF k = AST.NkReturn THEN
- IF AST.Child(seq, 0) = AST.NoNode THEN
- QbeGen.EmitRet(q, FALSE)
- ELSE
- LowerExpr(AST.Child(seq, 0), t, q);
- IF (emitProc >= 0) AND (emitProc < VAL(INTEGER, nProcs))
- AND CompositeRes(procRes[emitProc]) THEN
- (* composite result: copy into the hidden destination *)
- QbeGen.AddrOf("__res", qt);
- IF SymTab.ClassOf(procRes[emitProc]) = SymTab.ClArray THEN
- QbeGen.CopyArray(qt, q, procRes[emitProc])
- ELSIF SymTab.ClassOf(procRes[emitProc]) = SymTab.ClSet THEN
- QbeGen.CopySet(qt, q, SymTab.SetWords(procRes[emitProc]),
- SymTab.SetWords(procRes[emitProc]))
- ELSE QbeGen.CopyRecord(qt, q, procRes[emitProc])
- END;
- QbeGen.EmitRet("0", FALSE)
- ELSE
- conv := (SymTab.ClassOf(SymTab.CurRes()) = SymTab.ClReal)
- AND SymTab.IsIntFamily(t);
- IF conv THEN QbeGen.ConvIR(q, qt); QbeGen.EmitRet(qt, TRUE)
- ELSE QbeGen.EmitRet(q, TRUE)
- END
- END
- END
- ELSIF k = AST.NkCase THEN LowerCase(seq)
- ELSIF k = AST.NkWith THEN LowerWith(seq)
- ELSIF k = AST.NkCall THEN
- IF IsMethodCall(seq) THEN LowerMethodCall(seq, t, q, FALSE)
- ELSE
- CalleeName(seq, qn);
- IF IsBuiltin(qn) THEN LowerBuiltinStmt(seq, qn)
- ELSE LowerCall(seq, t, q, FALSE)
- END
- END
- END
- END LowerStmt;
- PROCEDURE ReenterGlobals (seq: AST.Node);
- (* Re-registers a nested module's non-exported globals in the enclosing
- (still-open) module scope, so QbeGen.GlobalRef can resolve their
- "<outer>_<name>" symbols after the nested scope has been popped. *)
- VAR i, j: CARDINAL; c: AST.Node; nm: SymTab.Name; chunk, vn: AST.Node;
- BEGIN
- IF seq = AST.NoNode THEN RETURN END;
- i := 0;
- WHILE i < AST.NChild(seq) DO
- c := AST.Child(seq, i);
- IF i = AST.MaxChild - 1 THEN ReenterGlobals(c)
- ELSIF AST.Kind(c) = AST.NkDeclSeq THEN ReenterGlobals(c)
- ELSIF AST.Kind(c) = AST.NkVarDecl THEN
- chunk := c;
- WHILE chunk # AST.NoNode DO
- j := 0;
- WHILE j < AST.NChild(chunk) DO
- vn := AST.Child(chunk, j);
- IF AST.Kind(vn) = AST.NkIdent THEN
- Name(vn, nm);
- IF SymTab.ReenterModGlobal(nm, SymTab.KindVar, curUnitName) THEN
- SymTab.SetSymType(nm, AST.Ty(c))
- END
- END;
- INC(j)
- END;
- IF AST.NChild(chunk) > 0 THEN
- vn := AST.Child(chunk, AST.NChild(chunk) - 1);
- IF AST.Kind(vn) = AST.NkBlock THEN chunk := vn
- ELSE chunk := AST.NoNode END
- ELSE chunk := AST.NoNode END
- END
- ELSIF (AST.Kind(c) = AST.NkTypeDecl)
- OR (AST.Kind(c) = AST.NkClassDecl) THEN
- (* a nested module's TYPE/CLASS: re-register it so ObjectSize and
- GlobalRef can resolve it after the nested scope is popped *)
- IF AST.NChild(c) >= 1 THEN
- Name(AST.Child(c, 0), nm);
- IF SymTab.ReenterModGlobal(nm, SymTab.KindType, curUnitName) THEN
- SymTab.SetSymType(nm, AST.Ty(c))
- END
- END
- END;
- INC(i)
- END
- END ReenterGlobals;
- PROCEDURE EmitVarNames (c: AST.Node);
- (* Emit DeclVar for each name of an NkVarDecl; the names are a chunked
- sequence (AstAppend), so follow NkBlock continuations. *)
- VAR j: CARDINAL; chunk, vn: AST.Node; nm: SymTab.Name;
- BEGIN
- chunk := c;
- WHILE chunk # AST.NoNode DO
- j := 0;
- WHILE j < AST.NChild(chunk) DO
- vn := AST.Child(chunk, j);
- IF AST.Kind(vn) = AST.NkIdent THEN
- Name(vn, nm);
- QbeGen.DeclVar(nm, AST.Ty(c));
- IF (NOT QbeGen.InFunction())
- AND (SymTab.ClassOf(AST.Ty(c)) = SymTab.ClArray)
- AND QbeGen.RecArrayNeedsInit(SymTab.ArrayLen(AST.Ty(c)),
- AST.Ty(c))
- AND (nCountInit <= HIGH(countArr)) THEN
- QbeGen.DeclRecTemplate(nm, SymTab.ArrayElem(AST.Ty(c)));
- countArr[nCountInit] := nm;
- countTyp[nCountInit] := AST.Ty(c);
- INC(nCountInit)
- END
- END;
- INC(j)
- END;
- IF AST.NChild(chunk) > 0 THEN
- vn := AST.Child(chunk, AST.NChild(chunk) - 1);
- IF AST.Kind(vn) = AST.NkBlock THEN chunk := vn
- ELSE chunk := AST.NoNode END
- ELSE chunk := AST.NoNode END
- END
- END EmitVarNames;
- PROCEDURE EmitCountInits;
- (* Initialise the length words of record-array globals that were emitted
- compactly (recorded by EmitVarNames). *)
- VAR k: CARDINAL;
- BEGIN
- k := 0;
- WHILE k < nCountInit DO
- QbeGen.InitRecArray(countArr[k], countTyp[k]);
- INC(k)
- END
- END EmitCountInits;
- PROCEDURE LowerDecls (seq: AST.Node);
- VAR i, j: CARDINAL; c: AST.Node; nm, txt: SymTab.Name; qv: QbeGen.QVal;
- mg: QbeGen.QVal; uid, depth, np: CARDINAL; res, pt: SymTab.TypeIndex;
- ext, pv: BOOLEAN; kk: INTEGER; ct: SymTab.TypeIndex; savedProc: INTEGER;
- chunk, vn: AST.Node; pi: INTEGER;
- mk: ARRAY [0 .. MaxCFoldW - 1] OF CARDINAL; mw: CARDINAL; mlo: INTEGER;
- BEGIN
- IF seq = AST.NoNode THEN RETURN END;
- i := 0;
- WHILE i < AST.NChild(seq) DO
- c := AST.Child(seq, i);
- IF i = AST.MaxChild - 1 THEN LowerDecls(c)
- ELSIF AST.Kind(c) = AST.NkDeclSeq THEN LowerDecls(c)
- ELSIF AST.Kind(c) = AST.NkClassDecl THEN
- Name(AST.Child(c, 0), nm);
- IF Resolve(nm, kk, ct) THEN
- IF SymTab.ClassOf(ct) = SymTab.ClClass THEN
- IF AST.Op(c) = 1 THEN
- LowerMethods(AST.Child(c, 3), ct);
- IF AST.Child(c, 2) # AST.NoNode THEN
- QbeGen.BeginInit(nm);
- LowerStmt(AST.Child(c, 2));
- QbeGen.EndInit
- END
- ELSE
- LowerMethodHeadings(AST.Child(c, 3), ct)
- END
- END
- END
- ELSIF AST.Kind(c) = AST.NkUnit THEN
- Name(AST.Child(c, 0), nm);
- ReenterGlobals(AST.Child(c, 1));
- LowerDecls(AST.Child(c, 1));
- IF AST.Child(c, 2) # AST.NoNode THEN
- QbeGen.BeginInit(nm);
- LowerStmt(AST.Child(c, 2));
- QbeGen.EndInit
- END
- ELSIF AST.Kind(c) = AST.NkVarDecl THEN
- (* op=1 marks a forward-typed global: emit it after the rest,
- as the grammar's FlushPend does *)
- IF AST.Op(c) # 1 THEN EmitVarNames(c) END
- ELSIF AST.Kind(c) = AST.NkConstDecl THEN
- Name(AST.Child(c, 0), nm);
- kk := SymTab.ClassOf(AST.Ty(c));
- IF kk = SymTab.ClSet THEN
- (* SET CONST: static data from the mask the grammar computed *)
- IF LookupSetMask(nm, mk, mw, mlo) THEN
- QbeGen.PoolSet(mk, mw, qv); SetConstVal(nm, qv)
- ELSE SetConstVal(nm, "0")
- END
- ELSIF (kk = SymTab.ClArray) OR (kk = SymTab.ClRecord)
- OR (kk = SymTab.ClClass) OR (kk = SymTab.ClStr)
- OR (kk = SymTab.ClUStr) THEN
- (* aggregate/string CONST: re-emit the constructor/string so
- its static data is produced (the grammar's CtorEnd/DeclStr
- are suppressed under NoEmit), then refresh the value *)
- LowerExpr(AST.Child(c, 1), ct, qv);
- SetConstVal(nm, qv)
- ELSIF GetConstVal(nm, qv) AND QbeGen.IsImm(qv) THEN
- (* scalar: use the evaluated value recorded by NoteConstVal *)
- QbeGen.DeclConst(nm, qv, AST.Ty(c))
- END
- ELSIF AST.Kind(c) = AST.NkProcDecl THEN
- Name(AST.Child(c, 0), nm);
- pi := AST.Ty(c); (* proc-table index recorded by NoteProcNode *)
- IF (pi < 0) OR (pi >= VAL(INTEGER, nProcs)) THEN pi := FindProc(nm) END;
- IF ProcInfoIdx(pi, uid, res, depth, ext, np) THEN
- QbeGen.Mangled(nm, uid, mg);
- QbeGen.BeginFunc(mg);
- IF CompositeRes(res) THEN
- IF NOT QbeGen.FuncParam("__res", TRUE, res) THEN END
- END;
- j := 0;
- WHILE j < np DO
- IF ParamInfoIdx(pi, j, pt, pv, txt) THEN
- IF NOT QbeGen.FuncParam(txt, pv OR SymTab.IsOpenArray(pt), pt) THEN END
- END;
- INC(j)
- END;
- QbeGen.SetFuncRes(res);
- IF AST.Child(c, 1) = AST.NoNode THEN
- (* declaration-only (definition module): replay the heading *)
- QbeGen.AbortFunc
- ELSE
- QbeGen.EndFuncHeader;
- savedProc := emitProc;
- emitProc := pi;
- LowerDecls(AST.Child(c, 2));
- nUserLab := 0;
- LowerStmt(AST.Child(c, 1));
- emitProc := savedProc;
- QbeGen.EndFunc(res)
- END
- END
- END;
- INC(i)
- END
- END LowerDecls;
- PROCEDURE MaterializeGlobals (unitName: ARRAY OF CHAR; decls: AST.Node);
- (* Make a unit's module-level VAR/CONST visible in the (global) scope
- Lower runs in at session end, so GlobalRef resolves "<unit>_<name>". *)
- VAR i, j: CARDINAL; c: AST.Node; nm: SymTab.Name; chunk, vn: AST.Node;
- BEGIN
- IF decls = AST.NoNode THEN RETURN END;
- i := 0;
- WHILE i < AST.NChild(decls) DO
- c := AST.Child(decls, i);
- IF i = AST.MaxChild - 1 THEN MaterializeGlobals(unitName, c)
- ELSIF AST.Kind(c) = AST.NkDeclSeq THEN MaterializeGlobals(unitName, c)
- ELSIF (AST.Kind(c) = AST.NkVarDecl) OR (AST.Kind(c) = AST.NkConstDecl) THEN
- chunk := c;
- WHILE chunk # AST.NoNode DO
- j := 0;
- WHILE j < AST.NChild(chunk) DO
- vn := AST.Child(chunk, j);
- IF AST.Kind(vn) = AST.NkIdent THEN
- Name(vn, nm);
- IF NOT SymTab.MaterializeReplace(unitName, nm) THEN END
- END;
- INC(j)
- END;
- IF AST.NChild(chunk) > 0 THEN
- vn := AST.Child(chunk, AST.NChild(chunk) - 1);
- IF AST.Kind(vn) = AST.NkBlock THEN chunk := vn
- ELSE chunk := AST.NoNode END
- ELSE chunk := AST.NoNode END
- END
- ELSIF (AST.Kind(c) = AST.NkTypeDecl) OR (AST.Kind(c) = AST.NkClassDecl) THEN
- IF AST.NChild(c) >= 1 THEN
- Name(AST.Child(c, 0), nm);
- IF NOT SymTab.MaterializeReplace(unitName, nm) THEN END
- END
- END;
- INC(i)
- END
- END MaterializeGlobals;
- PROCEDURE LowerDeferredVars (seq: AST.Node);
- (* Emit the forward-typed globals (NkVarDecl op=1) after the rest of the
- unit's declarations, matching the grammar's FlushPend ordering. *)
- VAR i: CARDINAL; c: AST.Node;
- BEGIN
- IF seq = AST.NoNode THEN RETURN END;
- i := 0;
- WHILE i < AST.NChild(seq) DO
- c := AST.Child(seq, i);
- IF i = AST.MaxChild - 1 THEN LowerDeferredVars(c)
- ELSIF AST.Kind(c) = AST.NkDeclSeq THEN LowerDeferredVars(c)
- ELSIF (AST.Kind(c) = AST.NkVarDecl) AND (AST.Op(c) = 1) THEN
- EmitVarNames(c)
- END;
- INC(i)
- END
- END LowerDeferredVars;
- PROCEDURE LowerOneUnit (u: AST.Node; isProg: BOOLEAN);
- VAR nm: SymTab.Name;
- BEGIN
- nCountInit := 0;
- Name(AST.Child(u, 0), nm);
- CopySpan(nm, curUnitName, 0, 0);
- QbeGen.SetModule(nm);
- MaterializeGlobals(nm, AST.Child(u, 1));
- LowerDecls(AST.Child(u, 1));
- LowerDeferredVars(AST.Child(u, 1));
- IF isProg THEN
- QbeGen.BeginBody;
- EmitCountInits;
- nUserLab := 0;
- LowerStmt(AST.Child(u, 2))
- ELSIF AST.Child(u, 2) # AST.NoNode THEN
- QbeGen.BeginInit(nm);
- EmitCountInits;
- nUserLab := 0;
- LowerStmt(AST.Child(u, 2));
- QbeGen.EndInit
- ELSIF nCountInit > 0 THEN
- QbeGen.BeginInit(nm);
- EmitCountInits;
- QbeGen.EndInit
- END
- END LowerOneUnit;
- PROCEDURE BeginSession (progName: ARRAY OF CHAR);
- (* Per-unit Lower emission. The grammar still runs its inline emit while
- parsing, but under NoEmit it only mutates QbeGen's counters (nothing
- reaches the buffer). For each unit: BeginUnit snapshots the state
- *before* parsing; after parsing, LowerUnitNow restores that snapshot
- and lets Lower re-emit the unit from the same counter values the
- legacy inline path would have used, so the image stays byte-identical.
- EndSession writes the accumulated session image. *)
- BEGIN
- withTop := 0;
- curImplCls := SymTab.InvalidType;
- scopeTop := 0; curScopeProc := -1; emitProc := -1;
- QbeGen.OpenModule(progName);
- QbeGen.SetNoEmit(TRUE)
- END BeginSession;
- PROCEDURE BeginUnit;
- (* Snapshot the pre-parse state so Lower can discard the grammar's
- suppressed inline mutations for this unit. *)
- BEGIN
- nImps := 0;
- QbeGen.SaveSession
- END BeginUnit;
- PROCEDURE LowerUnitNow (unit: AST.Node; isProg: BOOLEAN);
- BEGIN
- IF unit = AST.NoNode THEN RETURN END;
- QbeGen.RestoreSession;
- QbeGen.SetNoEmit(FALSE);
- LowerOneUnit(unit, isProg);
- QbeGen.SetNoEmit(TRUE)
- END LowerUnitNow;
- PROCEDURE EndSession;
- BEGIN
- QbeGen.SetNoEmit(FALSE);
- QbeGen.EndModule(curUnitName)
- END EndSession;
- BEGIN
- scopeTop := 0; curScopeProc := -1; emitProc := -1
- END Lower.
|