// callchain-s.txt
//		analyse control flow to determine
//		whether a function return a value

enum
    #f UD   // -1  undefine, don't know

// -,pr,prc,getc,gets,fopen,fclose,fpr,fprc,fgetc,
// fgets,loadfile,eval,exit,alloc,load
sysret = array
    0 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0

// function k at i
// ret 0 no, 1 yes, -1 don't know
to isRetv i k | a v =
    a = XARG[i]
    case XOP[i]
        icPut: v = 0
        icSt:  v = 0
        icStx: v = 0
        icSty: v = 0
        icSys: v = sysret[a]
        icCall:
            v = UD
            if (getRef k) != a  // prevent self call
                setCallee k a
                if (getRef a) != 0
                    v = getRetv a
        icJmp: v = 0
        icJt:  v = 0
        icJf:  v = 0
        icInc: v = UD
        icDec: v = UD
        icRet: v = UD
        else: v = 1
    v

// return 0 ret, 1 retv, -1 unknown
// check all ret until known
// assume all paths to ret are consistent
// only 90% correct, require CDFG analysis to fully correct

to findRet2 k | i v op =
    v = UD
    for i (getRef k)+1 (getEnd k)
        op = XOP[i]
        if op == icCase
            i = skipCase i		// skip jump table
        else if op == icRet
            v = isRetv i-1 k
            if v != UD break
    v

to findRet k | e1 v =
    e1 = (getEnd k) - 1
    // check tail call
    if and (XOP[e1] == icJmp) (XARG[e1] == ((getRef k)+1))
        v = UD					// force full check
    else
        v = isRetv e1 k
    if v == UD
        v = findRet2 k
    v

// update retv in the call chain
to callChain k | i j v =
    i = k
    j = getCallee i
    while j != NIL
        v = getRetv j
        if v != UD
            setRetv k v
            setRetv i v
            break
        i = j
        j = getCallee i

//to dumplis lis | i =
//	for i 1 (sizeoflis lis)
//		if lis[i] != 0
//			prints getName lis[i] nl

// TF after the instruction is executed
TF_state = array
    // 0..59  bop
    1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    // . get..sys0
    0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 1 0 0 1 1
    0 0 0 1 0 0 1 1 1 1 1 0 0

// convert code to be executed
to convert start end | i a op TF =
    TF = 0
    for i start end
        op = XOP[i]
        a = XARG[i]
        case op
            icLdy:
                TF = 1
                XARG[i] = getRef a
            icSty:
                TF = 0
                XARG[i] = getRef a
            icSt:
                TF = 0
                XARG[i] = getRef a
            icFun:
                TF = 0
                XARG[i] = (getLv a)-(getArity a)+1

            // special code
            icGet:
                if TF == 0
                    XOP[i] = icGet0
                    TF = 1
            icLd:
                if TF == 0
                    XOP[i] = icLd0
                    TF = 1
                XARG[i] = getRef a
            icLit:
                if TF == 0
                    XOP[i] = icLit0
                    TF = 1
            icAds:
                if TF == 0
                    XOP[i] = icAds0
                    TF = 1
            icPut:
                if TF == 1
                    XOP[i] = icPut0
                    TF = 0
            icCall:
                if and (TF == 0) ((getArity a) == 0)
                    XOP[i] = icCall0
                if (getRetv a) == UD
                    callChain a
                TF = getRetv a
                XARG[i] = getRef a
            icSys:
                if and (a == 3) (TF == 0)
                    XOP[i] = icSys0
                TF = sysret[a]
            else:
                TF = TF_state[op]
        if op == icCase
            i = skipCase i

// end
