/* * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. * See the copyright notice in the ACK home directory, in the file "Copyright". * * Author: Ceriel J.H. Jacobs */ /* C O D E G E N E R A T I O N R O U T I N E S */ /* $Id$ */ /* Code generation for expressions and coercions */ #include "parameters.h" #include "debug.h" #include #include "em_arith.h" #include "em_label.h" #include "em_code.h" #include "em_abs.h" #include "alloc.h" #include "code.h" #include "type.h" #include "error.h" #include "LLlex.h" #include "def.h" #include "scope.h" #include "desig.h" #include "chk_expr.h" #include "node.h" #include "Lpars.h" #include "tmpvar.h" #include "standards.h" #include "walk.h" extern int proclevel; extern char options[]; extern struct desig null_desig; int fp_used; /* Forward declarations */ static void CodeParameters(struct paramlist *, register struct node *); static void CodeStd(struct node *); static void compare(int, label); static void truthvalue(int); static void CodeUoper(register struct node *); static void CodeSet(register struct node *, int); static void CodeEl(register struct node *, register struct type *, int); static void CodeDAddress(struct node *, int); static void DoHIGH(register struct def *); void CodeConst(arith cst, int size) { if (size <= (int) word_size) { C_loc(cst); } else if (size == (int) dword_size) { C_ldc(cst); } else { crash("(CodeConst)"); } } void CodeString(register struct node *nd) { if (nd->nd_type->tp_fund != T_STRING) { /* Character constant */ CodeConst(nd->nd_INT, nd->nd_type->tp_size); return; } C_df_dlb(++data_label); C_rom_scon(nd->nd_STR, WA((arith)(nd->nd_SLE + 1))); c_lae_dlb(data_label); } void CodeExpr(struct node *nd, struct desig *ds, label true_label, label false_label) { register struct type *tp = nd->nd_type; DoLineno(nd); if (tp->tp_fund == T_REAL) fp_used = 1; switch(nd->nd_class) { case Def: if (nd->nd_def->df_kind & (D_PROCEDURE|D_PROCHEAD)) { C_lpi(nd->nd_def->prc_name); ds->dsg_kind = DSG_LOADED; break; } /* Fall through */ case Link: case Arrsel: case Arrow: CodeDesig(nd, ds); break; case Oper: CodeOper(nd, true_label, false_label); ds->dsg_kind = DSG_LOADED; true_label = NO_LABEL; break; case Uoper: CodeUoper(nd); ds->dsg_kind = DSG_LOADED; break; case Value: switch(nd->nd_symb) { case REAL: C_df_dlb(++data_label); if (! nd->nd_RSTR) { static char buf[FLT_STRLEN]; flt_flt2str(&nd->nd_RVAL, buf, FLT_STRLEN); C_rom_fcon(buf, tp->tp_size); } else C_rom_fcon(nd->nd_RSTR, tp->tp_size); c_lae_dlb(data_label); C_loi(tp->tp_size); break; case STRING: CodeString(nd); break; case INTEGER: CodeConst(nd->nd_INT, (int) (tp->tp_size)); break; default: crash("Value error"); } ds->dsg_kind = DSG_LOADED; break; case Call: CodeCall(nd); ds->dsg_kind = DSG_LOADED; break; case Set: { register unsigned i = (unsigned) (tp->tp_size) / (int) word_size; register arith *st = nd->nd_set + i; int null_set = 1; ds->dsg_kind = DSG_LOADED; for (; i; i--) { if (*--st != 0) null_set = 0; } if (! null_set) { i = (unsigned) (tp->tp_size) / (int) word_size; st = nd->nd_set + i; for (; i; i--) { C_loc(*--st); } } FreeSet(nd->nd_set); CodeSet(nd, null_set); } break; default: crash("(CodeExpr) bad node type"); } if (true_label != NO_LABEL) { /* Only for boolean expressions */ CodeValue(ds, tp); C_zne(true_label); c_bra(false_label); } } void CodeCoercion(struct type *t1, struct type *t2) { int fund1, fund2; int sz1 = t1->tp_size; int sz2; t1 = BaseType(t1); t2 = BaseType(t2); sz2 = t2->tp_size; switch(fund1 = t1->tp_fund) { case T_WORD: fund1 = T_INTEGER; break; case T_CHAR: case T_ENUMERATION: case T_CARDINAL: case T_INTORCARD: if (sz1 < (int) word_size) sz1 = word_size; /* fall through */ case T_EQUAL: case T_POINTER: fund1 = T_CARDINAL; break; } switch(fund2 = t2->tp_fund) { case T_WORD: fund2 = T_INTEGER; break; case T_CHAR: case T_ENUMERATION: sz2 = word_size; /* fall through */ case T_EQUAL: case T_POINTER: fund2 = T_CARDINAL; break; } switch(fund1) { case T_INTEGER: if (sz1 < (int) word_size) { c_loc(sz1); c_loc((int) word_size); C_cii(); sz1 = word_size; } c_loc(sz1); c_loc(sz2); switch(fund2) { case T_REAL: C_cif(); break; case T_INTEGER: C_cii(); break; case T_CARDINAL: C_ciu(); break; default: crash("Funny integer conversion"); } break; case T_CARDINAL: case T_INTORCARD: c_loc(sz1); c_loc(sz2); switch(fund2) { case T_REAL: C_cuf(); break; case T_CARDINAL: case T_INTORCARD: C_cuu(); break; case T_INTEGER: C_cui(); break; default: crash("Funny cardinal conversion"); } break; case T_REAL: switch(fund2) { case T_REAL: c_loc(sz1); c_loc(sz2); C_cff(); break; case T_INTEGER: c_loc(sz1); c_loc(sz2); C_cfi(); break; case T_CARDINAL: if (! options['R']) { label lb = ++text_label; arith asz1 = sz1; C_dup(asz1); C_zrf(asz1); C_cmf(asz1); C_zge(lb); c_loc(ECONV); C_trp(); def_ilb(lb); } c_loc(sz1); c_loc(sz2); C_cfu(); break; default: crash("Funny REAL conversion"); } break; } } void CodeCall(struct node *nd) { register struct node *left = nd->nd_LEFT; struct type *result_tp; int needs_fn; if (left->nd_type == std_type) { CodeStd(nd); return; } assert(IsProc(left)); result_tp = ResultType(left->nd_type); #ifdef BIG_RESULT_ON_STACK if (result_tp && TooBigForReturnArea(result_tp)) { C_asp(-WA(result_tp->tp_size)); } #endif if (nd->nd_RIGHT) { CodeParameters(ParamList(left->nd_type), nd->nd_RIGHT); } switch(left->nd_class) { case Def: { register struct def *df = left->nd_def; if (df->df_kind == D_CONST) { /* a procedure address */ df = df->con_const.tk_data.tk_def; } if (df->df_kind & (D_PROCEDURE|D_PROCHEAD)) { int level = df->df_scope->sc_level; if (level > 0) { C_lxl((arith) (proclevel - level)); } needs_fn = df->df_scope->sc_defmodule; C_cal(df->prc_name); break; }} /* Fall through */ default: needs_fn = 1; CodePExpr(left); C_cai(); } C_asp(left->nd_type->prc_nbpar); if (result_tp) { arith sz = WA(result_tp->tp_size); if (TooBigForReturnArea(result_tp)) { #ifndef BIG_RESULT_ON_STACK C_lfr(pointer_size); C_loi(sz); #endif } else C_lfr(sz); } DoFilename(needs_fn); DoLineno(nd); } /* Generates code to setup the parameters of a procedure call. */ static void CodeParameters(struct paramlist *param, register struct node *arg) { register struct type *tp; register struct type *arg_type; assert(param != 0 && arg != 0); if (param->par_next) { CodeParameters(param->par_next, arg->nd_RIGHT); } tp = TypeOfParam(param); arg = arg->nd_LEFT; arg_type = arg->nd_type; if (IsConformantArray(tp)) { register struct type *elem = tp->arr_elem; C_loc(tp->arr_elsize); if (IsConformantArray(arg_type)) { DoHIGH(arg->nd_def); if (elem->tp_size != arg_type->arr_elem->tp_size) { /* This can only happen if the formal type is ARRAY OF (WORD|BYTE) */ C_loc(arg_type->arr_elem->tp_size); C_mlu(word_size); if (elem == word_type) { c_loc((int) word_size - 1); C_adu(word_size); c_loc((int) word_size - 1); C_and(word_size); } else { assert(elem == byte_type); } } } else if (arg->nd_symb == STRING) { c_loc((int) arg->nd_SLE - 1); } else if (elem == word_type) { C_loc((arg_type->tp_size+word_size-1) / word_size - 1); } else if (elem == byte_type) { C_loc(arg_type->tp_size - 1); } else { C_loc(arg_type->arr_high - arg_type->arr_low); } c_loc(0); } if (IsConformantArray(tp) || IsVarParam(param)) { if (arg->nd_symb == STRING) { CodeString(arg); } else switch(arg->nd_class) { case Arrsel: case Arrow: case Def: CodeDAddress(arg, IsVarParam(param)); break; default:{ arith tmp, TmpSpace(); arith sz = WA(arg->nd_type->tp_size); CodePExpr(arg); tmp = TmpSpace(sz, arg->nd_type->tp_align); STL(tmp, sz); C_lal(tmp); } break; } return; } if (arg_type->tp_fund == T_STRING) { CodePString(arg, tp); return; } CodePExpr(arg); } void CodePString(struct node *nd, struct type *tp) { arith szarg = WA(nd->nd_type->tp_size); register arith zersz = WA(tp->tp_size) - szarg; if (zersz) { /* null padding required */ assert(zersz > 0); C_zer(zersz); } CodeString(nd); /* push address of string */ C_loi(szarg); } static void subu(int sz) { if (! options['R']) { C_cal(sz == (int) word_size ? "subuchk" : "subulchk"); } C_sbu((arith) sz); } static void addu(int sz) { if (! options['R']) { C_cal(sz == (int) word_size ? "adduchk" : "addulchk"); } C_adu((arith)sz); } static int complex_lhs(register struct node *nd) { switch(nd->nd_class) { case Value: case Name: case Set: case Def: return 0; case Select: return complex_lhs(nd->nd_NEXT); default: return 1; } } /* Generate code for internal procedures */ static void CodeStd(struct node *nd) { register struct node *arg = nd->nd_RIGHT; register struct node *left = 0; register struct type *tp = 0; int std = nd->nd_LEFT->nd_def->df_value.df_stdname; if (arg) { left = arg->nd_LEFT; tp = BaseType(left->nd_type); arg = arg->nd_RIGHT; } switch(std) { case S_ORD: case S_VAL: CodePExpr(left); break; case S_ABS: CodePExpr(left); if (tp->tp_fund == T_INTEGER) { CAL((int)(tp->tp_size) == (int)int_size ? "absi" : "absl", (int)(tp->tp_size)); } else if (tp->tp_fund == T_REAL) { CAL((int)(tp->tp_size) == (int)float_size ? "absf" : "absd", (int)(tp->tp_size)); } C_lfr(tp->tp_size); break; case S_CAP: CodePExpr(left); C_cal("cap"); break; case S_HIGH: assert(IsConformantArray(tp)); DoHIGH(left->nd_def); break; case S_SIZE: case S_TSIZE: assert(IsConformantArray(tp)); DoHIGH(left->nd_def); C_inc(); C_loc(tp->arr_elem->tp_size); C_mlu(word_size); break; case S_ODD: CodePExpr(left); if ((int) tp->tp_size == (int) word_size) { c_loc(1); C_and(word_size); } else { assert(tp->tp_size == dword_size); C_ldc((arith) 1); C_and(dword_size); C_ior(word_size); } break; case S_ADR: CodeDAddress(left, 1); break; case S_DEC: case S_INC: { register arith size; int compl = complex_lhs(left); arith tmp = 0; size = left->nd_type->tp_size; if ((int) size < (int) word_size) size = word_size; if (compl) { tmp = NewPtr(); CodeDAddress(left, 1); STL(tmp, pointer_size); LOL(tmp, pointer_size); C_loi(left->nd_type->tp_size); } else CodePExpr(left); CodeCoercion(left->nd_type, tp); if (arg) { CodePExpr(arg->nd_LEFT); CodeCoercion(arg->nd_LEFT->nd_type, tp); } else { c_loc(1); CodeCoercion(intorcard_type, tp); } if (std == S_DEC) { if (tp->tp_fund == T_INTEGER) C_sbi(size); else subu((int) size); } else { if (tp->tp_fund == T_INTEGER) C_adi(size); else addu((int) size); } if ((int) size == (int) word_size) { RangeCheck(left->nd_type, tp->tp_fund == T_INTEGER ? int_type : card_type); } if (compl) { LOL(tmp, pointer_size); C_sti(left->nd_type->tp_size); FreePtr(tmp); } else CodeDStore(left); break; } case S_HALT: C_cal("halt"); break; case S_INCL: case S_EXCL: { int compl = complex_lhs(left); arith tmp = 0; if (compl) { tmp = NewPtr(); CodeDAddress(left, 1); STL(tmp, pointer_size); LOL(tmp, pointer_size); C_loi(left->nd_type->tp_size); } else CodePExpr(left); CodePExpr(arg->nd_LEFT); C_loc(tp->set_low); C_sbi(word_size); C_set(tp->tp_size); if (std == S_INCL) { C_ior(tp->tp_size); } else { C_com(tp->tp_size); C_and(tp->tp_size); } if (compl) { LOL(tmp, pointer_size); C_sti(left->nd_type->tp_size); FreePtr(tmp); } else CodeDStore(left); break; } default: crash("(CodeStd)"); } } static int needs_rangecheck(register struct type *tpl, struct type *tpr) { arith rlo, rhi; if (bounded(tpl)) { /* In this case we might need a range check. If both types are restricted. check the bounds to see wether we need a range check. We don't need one if the range of values of the right hand side is a subset of the range of values of the left hand side. */ if (bounded(tpr)) { getbounds(tpr, &rlo, &rhi); if (in_range(rlo, tpl) && in_range(rhi, tpl)) { return 0; } } return 1; } return 0; } void RangeCheck(register struct type *tpl, struct type *tpr) { arith rlo, rhi; if (options['R']) return; if (needs_rangecheck(tpl, tpr)) { genrck(tpl); return; } tpr = BaseType(tpr); if ((tpl->tp_fund == T_INTEGER && tpr->tp_fund == T_CARDINAL) || (tpr->tp_fund == T_INTEGER && tpl->tp_fund == T_CARDINAL)) { label lb = ++text_label; C_dup(tpr->tp_size); C_zer(tpr->tp_size); C_cmi(tpr->tp_size); C_zge(lb); c_loc(ECONV); C_trp(); def_ilb(lb); } } void Operands(register struct node *nd) { CodePExpr(nd->nd_LEFT); CodePExpr(nd->nd_RIGHT); DoLineno(nd); } void CodeOper( register struct node *expr, /* the expression tree itself */ label true_label, label false_label /* labels to jump to in logical expr's */ ) { register struct node *leftop = expr->nd_LEFT; register struct node *rightop = expr->nd_RIGHT; int fund = expr->nd_type->tp_fund; arith size = expr->nd_type->tp_size; switch (expr->nd_symb) { case '+': Operands(expr); switch (fund) { case T_INTEGER: C_adi(size); break; case T_REAL: C_adf(size); break; case T_POINTER: case T_EQUAL: C_ads(rightop->nd_type->tp_size); break; case T_CARDINAL: case T_INTORCARD: addu((int) size); break; case T_SET: C_ior(size); break; default: crash("bad type +"); } break; case '-': Operands(expr); switch (fund) { case T_INTEGER: C_sbi(size); break; case T_REAL: C_sbf(size); break; case T_POINTER: case T_EQUAL: if (rightop->nd_type == address_type) { C_sbs(size); break; } C_ngi(rightop->nd_type->tp_size); C_ads(rightop->nd_type->tp_size); break; case T_INTORCARD: case T_CARDINAL: subu((int) size); break; case T_SET: C_com(size); C_and(size); break; default: crash("bad type -"); } break; case '*': Operands(expr); switch (fund) { case T_INTEGER: C_mli(size); break; case T_POINTER: case T_EQUAL: case T_CARDINAL: case T_INTORCARD: if (! options['R']) { C_cal((int)(size) <= (int)word_size ? "muluchk" : "mululchk"); } C_mlu(size); break; case T_REAL: C_mlf(size); break; case T_SET: C_and(size); break; default: crash("bad type *"); } break; case '/': Operands(expr); switch (fund) { case T_REAL: C_dvf(size); break; case T_SET: C_xor(size); break; default: crash("bad type /"); } break; case DIV: Operands(expr); switch(fund) { case T_INTEGER: C_cal((int)(size) == (int)word_size ? "dvi" : "dvil"); C_asp(2*size); C_lfr(size); break; case T_POINTER: case T_EQUAL: case T_CARDINAL: case T_INTORCARD: C_dvu(size); break; default: crash("bad type DIV"); } break; case MOD: Operands(expr); switch(fund) { case T_INTEGER: C_cal((int)(size) == (int)word_size ? "rmi" : "rmil"); C_asp(2*size); C_lfr(size); break; case T_POINTER: case T_EQUAL: case T_CARDINAL: case T_INTORCARD: C_rmu(size); break; default: crash("bad type MOD"); } break; case '<': case LESSEQUAL: case '>': case GREATEREQUAL: case '=': case '#': { struct type *tp; Operands(expr); tp = BaseType(leftop->nd_type); if (tp->tp_fund == T_INTORCARD) tp = BaseType(rightop->nd_type); size = tp->tp_size; switch (tp->tp_fund) { case T_INTEGER: C_cmi(size); break; case T_POINTER: case T_HIDDEN: case T_EQUAL: C_cmp(); break; case T_CARDINAL: case T_INTORCARD: C_cmu(size); break; case T_ENUMERATION: case T_CHAR: C_cmu(word_size); break; case T_REAL: C_cmf(size); break; case T_SET: if (expr->nd_symb == GREATEREQUAL) { /* A >= B is the same as A equals A + B */ C_dup(size << 1); C_asp(size); C_ior(size); expr->nd_symb = '='; } else if (expr->nd_symb == LESSEQUAL) { /* A <= B is the same as A - B = {} */ C_com(size); C_and(size); C_zer(size); expr->nd_symb = '='; } C_cms(size); break; default: crash("bad type COMPARE"); } if (true_label != NO_LABEL) { compare(expr->nd_symb, true_label); c_bra(false_label); break; } truthvalue(expr->nd_symb); break; } case IN: { /* In this case, evaluate right hand side first! The INN instruction expects the bit number on top of the stack */ label l_toolarge = NO_LABEL, l_cont = NO_LABEL; struct type *ltp = leftop->nd_type; if (leftop->nd_symb == COERCION) { /* Could be coercion to word_type. */ ltp = leftop->nd_RIGHT->nd_type; } if (leftop->nd_class == Value) { if (! in_range(leftop->nd_INT, ElementType(rightop->nd_type))) { if (true_label != NO_LABEL) { c_bra(false_label); } else c_loc(0); break; } CodePExpr(rightop); C_loc(leftop->nd_INT - rightop->nd_type->set_low); } else { CodePExpr(rightop); CodePExpr(leftop); C_loc(rightop->nd_type->set_low); C_sbu(word_size); if (needs_rangecheck(ElementType(rightop->nd_type), ltp)) { l_toolarge = ++text_label; C_dup(word_size); C_loc(rightop->nd_type->tp_size*8); C_cmu(word_size); C_zge(l_toolarge); } } C_inn(rightop->nd_type->tp_size); if (true_label != NO_LABEL) { C_zne(true_label); c_bra(false_label); } else { l_cont = ++text_label; c_bra(l_cont); } if (l_toolarge != NO_LABEL) { def_ilb(l_toolarge); C_asp(word_size+rightop->nd_type->tp_size); if (true_label != NO_LABEL) { c_bra(false_label); } else c_loc(0); } if (l_cont != NO_LABEL) { def_ilb(l_cont); } break; } case OR: case AND: { label l_maybe = ++text_label, l_end = NO_LABEL; struct desig Des; Des = null_desig; if (true_label == NO_LABEL) { true_label = ++text_label; false_label = ++text_label; l_end = ++text_label; } if (expr->nd_symb == OR) { CodeExpr(leftop, &Des, true_label, l_maybe); } else CodeExpr(leftop, &Des, l_maybe, false_label); def_ilb(l_maybe); Des = null_desig; CodeExpr(rightop, &Des, true_label, false_label); if (l_end != NO_LABEL) { def_ilb(true_label); c_loc(1); c_bra(l_end); def_ilb(false_label); c_loc(0); def_ilb(l_end); } break; } default: crash("(CodeOper) Bad operator"); } } /* Serves as an auxiliary function of CodeOper */ static void compare(int relop, label lbl) { switch (relop) { case '<': C_zlt(lbl); break; case LESSEQUAL: C_zle(lbl); break; case '>': C_zgt(lbl); break; case GREATEREQUAL: C_zge(lbl); break; case '=': C_zeq(lbl); break; case '#': C_zne(lbl); break; default: crash("(compare)"); } } /* Serves as an auxiliary function of CodeOper */ static void truthvalue(int relop) { switch (relop) { case '<': C_tlt(); break; case LESSEQUAL: C_tle(); break; case '>': C_tgt(); break; case GREATEREQUAL: C_tge(); break; case '=': C_teq(); break; case '#': C_tne(); break; default: crash("(truthvalue)"); } } /* Generates code for an unary expression */ void CodeUoper(register struct node *nd) { register struct type *tp = nd->nd_type; CodePExpr(nd->nd_RIGHT); switch(nd->nd_symb) { case NOT: C_teq(); break; case '-': switch(tp->tp_fund) { case T_INTEGER: case T_INTORCARD: C_ngi(tp->tp_size); break; case T_REAL: C_ngf(tp->tp_size); break; default: crash("Bad operand to unary -"); } break; case COERCION: CodeCoercion(nd->nd_RIGHT->nd_type, tp); RangeCheck(tp, nd->nd_RIGHT->nd_type); break; case CAST: break; default: crash("Bad unary operator"); } } static void CodeSet(register struct node *nd, int null_set) { register struct type *tp = nd->nd_type; nd = nd->nd_NEXT; while (nd) { assert(nd->nd_class == Link && nd->nd_symb == ','); if (nd->nd_LEFT) { CodeEl(nd->nd_LEFT, tp, null_set); null_set = 0; } nd = nd->nd_RIGHT; } if (null_set) C_zer(tp->tp_size); } static void CodeEl(register struct node *nd, register struct type *tp, int null_set) { register struct type *eltype = ElementType(tp); if (nd->nd_class == Link && nd->nd_symb == UPTO) { if (null_set) C_zer(tp->tp_size); C_loc(tp->set_low); C_loc(tp->tp_size); /* push size */ if (eltype->tp_fund == T_SUBRANGE) { C_loc(eltype->sub_ub); } else C_loc(eltype->enm_ncst - 1); Operands(nd); CAL("LtoUset", 5 * (int) word_size); /* library routine to fill set */ } else { CodePExpr(nd); C_loc(tp->set_low); C_sbi(word_size); C_set(tp->tp_size); if (! null_set) C_ior(tp->tp_size); } } void CodePExpr(register struct node *nd) { struct desig designator; designator = null_desig; CodeExpr(nd, &designator, NO_LABEL, NO_LABEL); CodeValue(&designator, nd->nd_type); } static void CodeDAddress(struct node *nd, int chk_controlvar) { /* Generate code to push the address of the designator "nd" on the stack. */ struct desig designator; int chkptr; designator = null_desig; if (chk_controlvar) ChkForFOR(nd); CodeDesig(nd, &designator); chkptr = designator.dsg_kind==DSG_PLOADED || designator.dsg_kind==DSG_PFIXED; CodeAddress(&designator); /* Generate dummy use of pointer, to get possible error message as soon as possible */ if (chkptr && ! options['R']) { C_dup(pointer_size); C_loi((arith) 1); C_asp(word_size); } } void CodeDStore(register struct node *nd) { struct desig designator; designator = null_desig; ChkForFOR(nd); CodeDesig(nd, &designator); CodeStore(&designator, nd->nd_type); } static void DoHIGH(register struct def *df) { /* Get the high index of a conformant array, indicated by "nd". The high index is the second field in the descriptor of the array, so it is easily found. */ register arith highoff; assert(df->df_kind == D_VARIABLE); assert(IsConformantArray(df->df_type)); highoff = df->var_off /* base address and descriptor */ + word_size + pointer_size; /* skip base and first field of descriptor */ if (df->df_scope->sc_level < proclevel) { C_lxa((arith) (proclevel - df->df_scope->sc_level)); C_lof(highoff); } else C_lol(highoff); } #ifdef SQUEEZE void c_bra(label l) { C_bra((label) l); } void c_loc(int n) { C_loc((arith) n); } void c_lae_dlb(label l) { C_lae_dlb(l, (arith) 0); } void CAL(char *name, int ssp) { C_cal(name); C_asp((arith) ssp); } #endif