1 /* Small compiler - code generation (unoptimized "assembler" code)
3 * Copyright (c) ITB CompuPhase, 1997-2003
5 * This software is provided "as-is", without any express or implied warranty.
6 * In no event will the authors be held liable for any damages arising from
7 * the use of this software.
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software in
15 * a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 * 2. Altered source versions must be plainly marked as such, and must not be
18 * misrepresented as being the original software.
19 * 3. This notice may not be removed or altered from any source distribution.
32 #include <limits.h> /* for PATH_MAX */
35 #include "embryo_cc_sc.h"
37 /* When a subroutine returns to address 0, the AMX must halt. In earlier
38 * releases, the RET and RETN opcodes checked for the special case 0 address.
39 * Today, the compiler simply generates a HALT instruction at address 0. So
40 * a subroutine can savely return to 0, and then encounter a HALT.
45 assert(code_idx == 0);
46 stgwrite(";program exit point\n");
47 stgwrite("\thalt 0\n");
48 /* calculate code length */
49 code_idx += opcodes(1) + opargs(1);
53 * Not much left of this once important function.
55 * Global references: sc_stksize (referred to only)
56 * sc_dataalign (referred to only)
58 * glb_declared (altered)
63 assert(sc_dataalign % opcodes(1) == 0); /* alignment must be a multiple of
65 assert(sc_dataalign != 0);
67 /* pad code to align data segment */
68 if ((code_idx % sc_dataalign) != 0)
71 while ((code_idx % sc_dataalign) != 0)
75 /* pad data segment to align the stack and the heap */
76 assert(litidx == 0); /* literal queue should have been emptied */
77 assert(sc_dataalign % sizeof(cell) == 0);
78 if (((glb_declared * sizeof(cell)) % sc_dataalign) != 0)
82 while (((glb_declared * sizeof(cell)) % sc_dataalign) != 0)
89 stgwrite("\nSTKSIZE "); /* write stack size (align stack top) */
90 outval(sc_stksize - (sc_stksize % sc_dataalign), TRUE);
94 * Start (or restart) the CODE segment.
96 * In fact, the code and data segment specifiers are purely informational;
97 * the "DUMP" instruction itself already specifies that the following values
98 * should go to the data segment. All otherinstructions go to the code
101 * Global references: curseg
106 if (curseg != sIN_CSEG)
109 stgwrite("CODE\t; ");
110 outval(code_idx, TRUE);
116 * Start (or restart) the DATA segment.
118 * Global references: curseg
123 if (curseg != sIN_DSEG)
126 stgwrite("DATA\t; ");
127 outval(glb_declared - litidx, TRUE);
133 setactivefile(int fnumber)
135 stgwrite("curfile ");
136 outval(fnumber, TRUE);
140 nameincells(char *name)
143 (strlen(name) + sizeof(cell)) & ~(sizeof(cell) - 1);
148 setfile(char *name, int fileno)
150 if ((sc_debug & sSYMBOLIC) != 0)
154 outval(fileno, FALSE);
158 /* calculate code length */
159 code_idx += opcodes(1) + opargs(2) + nameincells(name);
164 setline(int line, int fileno)
166 if ((sc_debug & (sSYMBOLIC | sCHKBOUNDS)) != 0)
171 outval(fileno, FALSE);
173 outval(code_idx, TRUE);
174 code_idx += opcodes(1) + opargs(2);
180 * Post a code label (specified as a number), on a new line.
187 stgwrite((char *)itoh(number));
188 /* To assist verification of the assembled code, put the address of the
189 * label as a comment. However, labels that occur inside an expression
190 * may move (through optimization or through re-ordering). So write the
191 * address only if it is known to accurate.
196 outval(code_idx, FALSE);
201 /* Write a token that signifies the end of an expression, or the end of a
202 * function parameter. This allows several simple optimizations by the peephole
206 endexpr(int fullexpr)
209 stgwrite("\t;$exp\n");
211 stgwrite("\t;$par\n");
214 /* startfunc - declare a CODE entry point (function start)
216 * Global references: funcstatus (referred to only)
219 startfunc(char *fname __UNUSED__)
223 code_idx += opcodes(1);
228 * Declare a CODE ending point (function end)
233 stgwrite("\n"); /* skip a line */
238 * Aligns the frame (and the stack) of the current function to a multiple
239 * of the specified byte count. Two caveats: the alignment ("numbytes") should
240 * be a power of 2, and this alignment must be done right after the frame
241 * is set up (before the first variable is declared)
244 alignframe(int numbytes)
247 /* "numbytes" should be a power of 2 for this code to work */
250 for (i = 0; i < (int)(sizeof(numbytes) * 8); i++)
251 if (numbytes & (1 << i))
256 stgwrite("\tlctrl 4\n"); /* get STK in PRI */
257 stgwrite("\tconst.alt "); /* get ~(numbytes-1) in ALT */
258 outval(~(numbytes - 1), TRUE);
259 stgwrite("\tand\n"); /* PRI = STK "and" ~(numbytes-1) */
260 stgwrite("\tsctrl 4\n"); /* set the new value of STK ... */
261 stgwrite("\tsctrl 5\n"); /* ... and FRM */
262 code_idx += opcodes(5) + opargs(4);
265 /* Define a variable or function
268 defsymbol(char *name, int ident, int vclass, cell offset, int tag)
270 if ((sc_debug & sSYMBOLIC) != 0)
272 begcseg(); /* symbol definition in code segment */
278 outval(offset, FALSE);
281 outval(vclass, FALSE);
286 code_idx += opcodes(1) + opargs(3) + nameincells(name); /* class and ident encoded in "flags" */
288 /* also write the optional tag */
291 assert((tag & TAGMASK) != 0);
293 outval(tag & TAGMASK, TRUE);
294 code_idx += opcodes(1) + opargs(1);
300 symbolrange(int level, cell size)
302 if ((sc_debug & sSYMBOLIC) != 0)
304 begcseg(); /* symbol definition in code segment */
306 outval(level, FALSE);
309 code_idx += opcodes(1) + opargs(2);
315 * Generate code to get the value of a symbol into "primary".
323 if (lval->ident == iARRAYCELL)
325 /* indirect fetch, address already in PRI */
326 stgwrite("\tload.i\n");
327 code_idx += opcodes(1);
329 else if (lval->ident == iARRAYCHAR)
331 /* indirect fetch of a character from a pack, address already in PRI */
332 stgwrite("\tlodb.i ");
333 outval(charbits / 8, TRUE); /* read one or two bytes */
334 code_idx += opcodes(1) + opargs(1);
336 else if (lval->ident == iREFERENCE)
338 /* indirect fetch, but address not yet in PRI */
340 assert(sym->vclass == sLOCAL); /* global references don't exist in Small */
341 if (sym->vclass == sLOCAL)
342 stgwrite("\tlref.s.pri ");
344 stgwrite("\tlref.pri ");
345 outval(sym->addr, TRUE);
346 markusage(sym, uREAD);
347 code_idx += opcodes(1) + opargs(1);
351 /* direct or stack relative fetch */
353 if (sym->vclass == sLOCAL)
354 stgwrite("\tload.s.pri ");
356 stgwrite("\tload.pri ");
357 outval(sym->addr, TRUE);
358 markusage(sym, uREAD);
359 code_idx += opcodes(1) + opargs(1);
364 * Get the address of a symbol into the primary register (used for arrays,
365 * and for passing arguments by reference).
368 address(symbol * sym)
371 /* the symbol can be a local array, a global array, or an array
372 * that is passed by reference.
374 if (sym->ident == iREFARRAY || sym->ident == iREFERENCE)
376 /* reference to a variable or to an array; currently this is
377 * always a local variable */
378 stgwrite("\tload.s.pri ");
382 /* a local array or local variable */
383 if (sym->vclass == sLOCAL)
384 stgwrite("\taddr.pri ");
386 stgwrite("\tconst.pri ");
388 outval(sym->addr, TRUE);
389 markusage(sym, uREAD);
390 code_idx += opcodes(1) + opargs(1);
395 * Saves the contents of "primary" into a memory cell, either directly
396 * or indirectly (at the address given in the alternate register).
404 if (lval->ident == iARRAYCELL)
406 /* store at address in ALT */
407 stgwrite("\tstor.i\n");
408 code_idx += opcodes(1);
410 else if (lval->ident == iARRAYCHAR)
412 /* store at address in ALT */
413 stgwrite("\tstrb.i ");
414 outval(charbits / 8, TRUE); /* write one or two bytes */
415 code_idx += opcodes(1) + opargs(1);
417 else if (lval->ident == iREFERENCE)
420 if (sym->vclass == sLOCAL)
421 stgwrite("\tsref.s.pri ");
423 stgwrite("\tsref.pri ");
424 outval(sym->addr, TRUE);
425 code_idx += opcodes(1) + opargs(1);
430 markusage(sym, uWRITTEN);
431 if (sym->vclass == sLOCAL)
432 stgwrite("\tstor.s.pri ");
434 stgwrite("\tstor.pri ");
435 outval(sym->addr, TRUE);
436 code_idx += opcodes(1) + opargs(1);
440 /* source must in PRI, destination address in ALT. The "size"
441 * parameter is in bytes, not cells.
449 code_idx += opcodes(1) + opargs(1);
452 /* Address of the source must already have been loaded in PRI
453 * "size" is the size in bytes (not cells).
456 copyarray(symbol * sym, cell size)
459 /* the symbol can be a local array, a global array, or an array
460 * that is passed by reference.
462 if (sym->ident == iREFARRAY)
464 /* reference to an array; currently this is always a local variable */
465 assert(sym->vclass == sLOCAL); /* symbol must be stack relative */
466 stgwrite("\tload.s.alt ");
470 /* a local or global array */
471 if (sym->vclass == sLOCAL)
472 stgwrite("\taddr.alt ");
474 stgwrite("\tconst.alt ");
476 outval(sym->addr, TRUE);
477 markusage(sym, uWRITTEN);
479 code_idx += opcodes(1) + opargs(1);
484 fillarray(symbol * sym, cell size, cell val)
486 const1(val); /* load val in PRI */
489 /* the symbol can be a local array, a global array, or an array
490 * that is passed by reference.
492 if (sym->ident == iREFARRAY)
494 /* reference to an array; currently this is always a local variable */
495 assert(sym->vclass == sLOCAL); /* symbol must be stack relative */
496 stgwrite("\tload.s.alt ");
500 /* a local or global array */
501 if (sym->vclass == sLOCAL)
502 stgwrite("\taddr.alt ");
504 stgwrite("\tconst.alt ");
506 outval(sym->addr, TRUE);
507 markusage(sym, uWRITTEN);
512 code_idx += opcodes(2) + opargs(2);
516 * Instruction to get an immediate value into the primary register
523 stgwrite("\tzero.pri\n");
524 code_idx += opcodes(1);
528 stgwrite("\tconst.pri ");
530 code_idx += opcodes(1) + opargs(1);
535 * Instruction to get an immediate value into the secondary register
542 stgwrite("\tzero.alt\n");
543 code_idx += opcodes(1);
547 stgwrite("\tconst.alt ");
549 code_idx += opcodes(1) + opargs(1);
553 /* Copy value in secondary register to the primary register */
557 stgwrite("\tmove.pri\n");
558 code_idx += opcodes(1) + opargs(0);
562 * Push primary register onto the stack
567 stgwrite("\tpush.pri\n");
568 code_idx += opcodes(1);
572 * Push alternate register onto the stack
577 stgwrite("\tpush.alt\n");
578 code_idx += opcodes(1);
582 * Push a constant value onto the stack
587 stgwrite("\tpush.c ");
589 code_idx += opcodes(1) + opargs(1);
593 * pop stack to the primary register
598 stgwrite("\tpop.pri\n");
599 code_idx += opcodes(1);
603 * pop stack to the secondary register
608 stgwrite("\tpop.alt\n");
609 code_idx += opcodes(1);
613 * swap the top-of-stack with the value in primary register
618 stgwrite("\tswap.pri\n");
619 code_idx += opcodes(1);
623 * The "switch" statement generates a "case" table using the "CASE" opcode.
624 * The case table contains a list of records, each record holds a comparison
625 * value and a label to branch to on a match. The very first record is an
626 * exception: it holds the size of the table (excluding the first record) and
627 * the label to branch to when none of the values in the case table match.
628 * The case table is sorted on the comparison value. This allows more advanced
629 * abstract machines to sift the case table with a binary search.
634 stgwrite("\tswitch ");
635 outval(label, TRUE); /* the label is the address of the case table */
636 code_idx += opcodes(1) + opargs(1);
640 ffcase(cell val, char *labelname, int newtable)
644 stgwrite("\tcasetbl\n");
645 code_idx += opcodes(1);
652 code_idx += opcodes(0) + opargs(2);
656 * Call specified function
659 ffcall(symbol * sym, int numargs)
662 assert(sym->ident == iFUNCTN);
663 if ((sym->usage & uNATIVE) != 0)
665 /* reserve a SYSREQ id if called for the first time */
666 if (sc_status == statWRITE && (sym->usage & uREAD) == 0
668 sym->addr = ntv_funcid++;
669 stgwrite("\tsysreq.c ");
670 outval(sym->addr, FALSE);
671 stgwrite("\n\tstack ");
672 outval((numargs + 1) * sizeof(cell), TRUE);
673 code_idx += opcodes(2) + opargs(2);
677 /* normal function */
681 code_idx += opcodes(1) + opargs(1);
685 /* Return from function
687 * Global references: funcstatus (referred to only)
692 stgwrite("\tretn\n");
693 code_idx += opcodes(1);
700 outval(reason, TRUE);
701 code_idx += opcodes(1) + opargs(1);
707 if ((sc_debug & sCHKBOUNDS) != 0)
709 stgwrite("\tbounds ");
711 code_idx += opcodes(1) + opargs(1);
716 * Jump to local label number (the number is converted to a name)
719 jumplabel(int number)
722 outval(number, TRUE);
723 code_idx += opcodes(1) + opargs(1);
727 * Define storage (global and static variables)
736 * Inclrement/decrement stack pointer. Note that this routine does
737 * nothing if the delta is zero.
744 stgwrite("\tstack ");
746 code_idx += opcodes(1) + opargs(1);
750 /* set the stack to a hard offset from the frame */
754 stgwrite("\tlctrl 5\n"); /* get FRM */
755 assert(val <= 0); /* STK should always become <= FRM */
758 stgwrite("\tadd.c ");
759 outval(val, TRUE); /* add (negative) offset */
760 code_idx += opcodes(1) + opargs(1);
761 // ??? write zeros in the space between STK and the val in PRI (the new stk)
762 // get val of STK in ALT
764 // need new FILL opcode that takes a variable size
766 stgwrite("\tsctrl 4\n"); /* store in STK */
767 code_idx += opcodes(2) + opargs(2);
777 code_idx += opcodes(1) + opargs(1);
784 stgwrite("\theap "); /* ALT = HEA++ */
785 outval(sizeof(cell), TRUE);
786 stgwrite("\tstor.i\n"); /* store PRI (default value) at address ALT */
787 stgwrite("\tmove.pri\n"); /* move ALT to PRI: PRI contains the address */
788 code_idx += opcodes(3) + opargs(1);
794 stgwrite("\tconst.pri "); /* load default val in PRI */
796 code_idx += opcodes(1) + opargs(1);
801 * Convert a cell number to a "byte" address; i.e. double or quadruple
802 * the primary register.
808 stgwrite("\tshl.c.pri 1\n");
810 stgwrite("\tshl.c.pri 2\n");
812 code_idx += opcodes(1) + opargs(1);
816 * Double or quadruple the alternate register.
822 stgwrite("\tshl.c.alt 1\n");
824 stgwrite("\tshl.c.alt 2\n");
826 code_idx += opcodes(1) + opargs(1);
830 * Convert "distance of addresses" to "number of cells" in between.
831 * Or convert a number of packed characters to the number of cells (with
838 stgwrite("\tshr.c.pri 1\n");
840 stgwrite("\tshr.c.pri 2\n");
842 code_idx += opcodes(1) + opargs(1);
845 /* Convert from character index to byte address. This routine does
846 * nothing if a character has the size of a byte.
853 stgwrite("\tshl.c.pri 1\n");
854 code_idx += opcodes(1) + opargs(1);
858 /* Align PRI (which should hold a character index) to an address.
859 * The first character in a "pack" occupies the highest bits of
860 * the cell. This is at the lower memory address on Big Endian
861 * computers and on the higher address on Little Endian computers.
862 * The ALIGN.pri/alt instructions must solve this machine dependence;
863 * that is, on Big Endian computers, ALIGN.pri/alt shuold do nothing
864 * and on Little Endian computers they should toggle the address.
869 stgwrite("\talign.pri ");
870 outval(charbits / 8, TRUE);
871 code_idx += opcodes(1) + opargs(1);
875 * Add a constant to the primary register.
882 stgwrite("\tadd.c ");
884 code_idx += opcodes(1) + opargs(1);
889 * signed multiply of primary and secundairy registers (result in primary)
894 stgwrite("\tsmul\n");
895 code_idx += opcodes(1);
899 * signed divide of alternate register by primary register (quotient in
900 * primary; remainder in alternate)
905 stgwrite("\tsdiv.alt\n");
906 code_idx += opcodes(1);
910 * modulus of (alternate % primary), result in primary (signed)
915 stgwrite("\tsdiv.alt\n");
916 stgwrite("\tmove.pri\n"); /* move ALT to PRI */
917 code_idx += opcodes(2);
921 * Add primary and alternate registers (result in primary).
927 code_idx += opcodes(1);
931 * subtract primary register from alternate register (result in primary)
936 stgwrite("\tsub.alt\n");
937 code_idx += opcodes(1);
941 * arithmic shift left alternate register the number of bits
942 * given in the primary register (result in primary).
943 * There is no need for a "logical shift left" routine, since
944 * logical shift left is identical to arithmic shift left.
949 stgwrite("\txchg\n");
951 code_idx += opcodes(2);
955 * arithmic shift right alternate register the number of bits
956 * given in the primary register (result in primary).
961 stgwrite("\txchg\n");
962 stgwrite("\tsshr\n");
963 code_idx += opcodes(2);
967 * logical (unsigned) shift right of the alternate register by the
968 * number of bits given in the primary register (result in primary).
973 stgwrite("\txchg\n");
975 code_idx += opcodes(2);
979 * inclusive "or" of primary and secondary registers (result in primary)
985 code_idx += opcodes(1);
989 * "exclusive or" of primary and alternate registers (result in primary)
995 code_idx += opcodes(1);
999 * "and" of primary and secundairy registers (result in primary)
1004 stgwrite("\tand\n");
1005 code_idx += opcodes(1);
1009 * test ALT==PRI; result in primary register (1 or 0).
1015 code_idx += opcodes(1);
1024 stgwrite("\tneq\n");
1025 code_idx += opcodes(1);
1028 /* The abstract machine defines the relational instructions so that PRI is
1029 * on the left side and ALT on the right side of the operator. For example,
1030 * SLESS sets PRI to either 1 or 0 depending on whether the expression
1031 * "PRI < ALT" is true.
1033 * The compiler generates comparisons with ALT on the left side of the
1034 * relational operator and PRI on the right side. The XCHG instruction
1035 * prefixing the relational operators resets this. We leave it to the
1036 * peephole optimizer to choose more compact instructions where possible.
1039 /* Relational operator prefix for chained relational expressions. The
1040 * "suffix" code restores the stack.
1041 * For chained relational operators, the goal is to keep the comparison
1042 * result "so far" in PRI and the value of the most recent operand in
1043 * ALT, ready for a next comparison.
1044 * The "prefix" instruction pushed the comparison result (PRI) onto the
1045 * stack and moves the value of ALT into PRI. If there is a next comparison,
1046 * PRI can now serve as the "left" operand of the relational operator.
1051 stgwrite("\tpush.pri\n");
1052 stgwrite("\tmove.pri\n");
1053 code_idx += opcodes(2);
1059 stgwrite("\tswap.alt\n");
1060 stgwrite("\tand\n");
1061 stgwrite("\tpop.alt\n");
1062 code_idx += opcodes(3);
1066 * test ALT<PRI (signed)
1071 stgwrite("\txchg\n");
1072 stgwrite("\tsless\n");
1073 code_idx += opcodes(2);
1077 * test ALT<=PRI (signed)
1082 stgwrite("\txchg\n");
1083 stgwrite("\tsleq\n");
1084 code_idx += opcodes(2);
1088 * test ALT>PRI (signed)
1093 stgwrite("\txchg\n");
1094 stgwrite("\tsgrtr\n");
1095 code_idx += opcodes(2);
1099 * test ALT>=PRI (signed)
1104 stgwrite("\txchg\n");
1105 stgwrite("\tsgeq\n");
1106 code_idx += opcodes(2);
1110 * logical negation of primary register
1115 stgwrite("\tnot\n");
1116 code_idx += opcodes(1);
1120 * two's complement primary register
1125 stgwrite("\tneg\n");
1126 code_idx += opcodes(1);
1130 * one's complement of primary register
1135 stgwrite("\tinvert\n");
1136 code_idx += opcodes(1);
1145 stgwrite("\tnop\n");
1146 code_idx += opcodes(1);
1157 if (lval->ident == iARRAYCELL)
1159 /* indirect increment, address already in PRI */
1160 stgwrite("\tinc.i\n");
1161 code_idx += opcodes(1);
1163 else if (lval->ident == iARRAYCHAR)
1165 /* indirect increment of single character, address already in PRI */
1166 stgwrite("\tpush.pri\n");
1167 stgwrite("\tpush.alt\n");
1168 stgwrite("\tmove.alt\n"); /* copy address */
1169 stgwrite("\tlodb.i "); /* read from PRI into PRI */
1170 outval(charbits / 8, TRUE); /* read one or two bytes */
1171 stgwrite("\tinc.pri\n");
1172 stgwrite("\tstrb.i "); /* write PRI to ALT */
1173 outval(charbits / 8, TRUE); /* write one or two bytes */
1174 stgwrite("\tpop.alt\n");
1175 stgwrite("\tpop.pri\n");
1176 code_idx += opcodes(8) + opargs(2);
1178 else if (lval->ident == iREFERENCE)
1180 assert(sym != NULL);
1181 stgwrite("\tpush.pri\n");
1182 /* load dereferenced value */
1183 assert(sym->vclass == sLOCAL); /* global references don't exist in Small */
1184 if (sym->vclass == sLOCAL)
1185 stgwrite("\tlref.s.pri ");
1187 stgwrite("\tlref.pri ");
1188 outval(sym->addr, TRUE);
1190 stgwrite("\tinc.pri\n");
1191 /* store dereferenced value */
1192 if (sym->vclass == sLOCAL)
1193 stgwrite("\tsref.s.pri ");
1195 stgwrite("\tsref.pri ");
1196 outval(sym->addr, TRUE);
1197 stgwrite("\tpop.pri\n");
1198 code_idx += opcodes(5) + opargs(2);
1202 /* local or global variable */
1203 assert(sym != NULL);
1204 if (sym->vclass == sLOCAL)
1205 stgwrite("\tinc.s ");
1208 outval(sym->addr, TRUE);
1209 code_idx += opcodes(1) + opargs(1);
1215 * in case of an integer pointer, the symbol must be incremented by 2.
1223 if (lval->ident == iARRAYCELL)
1225 /* indirect decrement, address already in PRI */
1226 stgwrite("\tdec.i\n");
1227 code_idx += opcodes(1);
1229 else if (lval->ident == iARRAYCHAR)
1231 /* indirect decrement of single character, address already in PRI */
1232 stgwrite("\tpush.pri\n");
1233 stgwrite("\tpush.alt\n");
1234 stgwrite("\tmove.alt\n"); /* copy address */
1235 stgwrite("\tlodb.i "); /* read from PRI into PRI */
1236 outval(charbits / 8, TRUE); /* read one or two bytes */
1237 stgwrite("\tdec.pri\n");
1238 stgwrite("\tstrb.i "); /* write PRI to ALT */
1239 outval(charbits / 8, TRUE); /* write one or two bytes */
1240 stgwrite("\tpop.alt\n");
1241 stgwrite("\tpop.pri\n");
1242 code_idx += opcodes(8) + opargs(2);
1244 else if (lval->ident == iREFERENCE)
1246 assert(sym != NULL);
1247 stgwrite("\tpush.pri\n");
1248 /* load dereferenced value */
1249 assert(sym->vclass == sLOCAL); /* global references don't exist in Small */
1250 if (sym->vclass == sLOCAL)
1251 stgwrite("\tlref.s.pri ");
1253 stgwrite("\tlref.pri ");
1254 outval(sym->addr, TRUE);
1256 stgwrite("\tdec.pri\n");
1257 /* store dereferenced value */
1258 if (sym->vclass == sLOCAL)
1259 stgwrite("\tsref.s.pri ");
1261 stgwrite("\tsref.pri ");
1262 outval(sym->addr, TRUE);
1263 stgwrite("\tpop.pri\n");
1264 code_idx += opcodes(5) + opargs(2);
1268 /* local or global variable */
1269 assert(sym != NULL);
1270 if (sym->vclass == sLOCAL)
1271 stgwrite("\tdec.s ");
1274 outval(sym->addr, TRUE);
1275 code_idx += opcodes(1) + opargs(1);
1280 * Jumps to "label" if PRI != 0
1286 outval(number, TRUE);
1287 code_idx += opcodes(1) + opargs(1);
1291 * Jumps to "label" if PRI == 0
1296 stgwrite("\tjzer ");
1297 outval(number, TRUE);
1298 code_idx += opcodes(1) + opargs(1);
1301 /* write a value in hexadecimal; optionally adds a newline */
1303 outval(cell val, int newline)
1305 stgwrite(itoh(val));