PreviousUpNext

15.4.511  src/lib/compiler/back/top/nextcode/nextcode-form.pkg

## nextcode-form.pkg 

# Compiled by:
#     src/lib/compiler/core.sublib



###             "He who walks with truth makes life."
###
###                                 -- Sumerian saying



stipulate
    package cty =  ctypes;                                              # ctypes                        is from   src/lib/compiler/back/low/ccalls/ctypes.pkg
    package hbt =  highcode_basetypes;                                  # highcode_basetypes            is from   src/lib/compiler/back/top/highcode/highcode-basetypes.pkg
    package tmp =  highcode_codetemp;                                   # highcode_codetemp             is from   src/lib/compiler/back/top/highcode/highcode-codetemp.pkg

    fun bug s
        =
        error_message::impossible ("nextcode:" + s);
herein


    package nextcode_form {                                             # Not sure why we don't seal here with   src/lib/compiler/back/top/nextcode/nextcode-form.api
        #

        package rk {
            Record_Kind                                                 # See comments in   src/lib/compiler/back/top/nextcode/nextcode-form.api
              = VECTOR
              | RECORD
              | SPILL
              # 
              | PUBLIC_FN
              | PRIVATE_FN
              | FATE_FN
              | FLOAT64_FATE_FN
              # 
              | FLOAT64_BLOCK
              | INT1_BLOCK
              ;
        };
        Record_Kind = rk::Record_Kind;

        Pkind = VPT | RPT  Int | FPT  Int;

        package typ {
            Type
              = INT             # 31-bit int?
              | INT1            # 32-bit int?
              | FLOAT64         # Float?
              | POINTER Pkind   # Pointer?
              | FUN             # Unsigned int?
              | FATE            # Fate?
              | DSP             #
              ;                                 # Empirically, ncftype_for_fun is either FATE, FUN or (POINTER VPT) in   convert_nextcode_public_fun_args_to_treecode  in  src/lib/compiler/back/low/main/nextcode/convert-nextcode-fun-args-to-treecode-g.pkg
        };
        Type = typ::Type;

        package p {
            #
            Number_Kind_And_Size
              #
              = INT   Int                       # Fixed-length   signed-integer type.
              | UNT   Int                       # Fixed-length unsigned-integer type.
              | FLOAT Int                       # Fixed-length floating-point   type.   
              ;

            Arithop
              = ADD
              | SUBTRACT
              | MULTIPLY
              # 
              | DIVIDE                          # Round-to-zero division -- this is the native instruction on Intel32.
              | DIV                             # Round-to-negative-infinity division  -- this will be much slower on Intel32, has to be faked.
              # 
              | REM                             # Round-to-zero remainder -- this is the native instruction on Intel32.
              | MOD                             # Round-to-negative-infinity remainder -- this will be much slower on Intel32, has to be faked.
              # 
              | NEGATE
              | ABS 
              | FSQRT
              | FSIN
              | FCOS
              | FTAN 
              | LSHIFT
              | RSHIFT
              | RSHIFTL
              | BITWISE_AND
              | BITWISE_OR
              | BITWISE_XOR
              | BITWISE_NOT
              ;

            Compare_Op = GT | GE | LT | LE | EQL | NEQ;

            # fcmpop conforms to the IEEE std 754 predicates.
            #
            package f {
                Ieee754_Floating_Point_Compare_Op 
                  = EQ          #  = 
                  | ULG         #  ?<> 
                  | UN          #  ? 
                  | LEG         #  <=>
                  | GT          #  > 
                  | GE          #  >= 
                  | UGT         #  ?> 
                  | UGE         #  ?>=
                  | LT          #  < 
                  | LE          #  <= 
                  | ULT         #  ?< 
                  | ULE         #  ?<=
                  | LG          #  <> 
                  | UE          #  ?= 
                  ;
            };
            Ieee754_Floating_Point_Compare_Op = f::Ieee754_Floating_Point_Compare_Op;

            # These are two-way branches
            # dependent on pure inputs.
            # See comments in   src/lib/compiler/back/top/nextcode/nextcode-form.api
            #
            Branch
              = COMPARE         { op: Compare_Op,                         kind_and_size: Number_Kind_And_Size   }    #  numkind cannot be FLOAT 
              | COMPARE_FLOATS  { op: Ieee754_Floating_Point_Compare_Op,  size:     Int                         }
              #
              | IS_BOXED
              | IS_UNBOXED
              #
              | POINTER_EQL
              | POINTER_NEQ
              #
              | STRING_EQL
              | STRING_NEQ
              ; 
                  # streq (n, a, b) is defined only if strings a and b have
                  # exactly the same length n > 1 

            # These overwrite existing values in ram.
            # (The "ram" might possibly be cached in registers.)
            #
            Store_To_Ram
              = SET_VECSLOT_TO_NUMERIC_VALUE { kind_and_size: Number_Kind_And_Size }
              | SET_VECSLOT_TO_TAGGED_INT_VALUE
              | SET_VECSLOT_TO_BOXED_VALUE                                              # Produces same code as next; used to store String and Float64 values into a vector.
              | RW_VECTOR_SET                                                           # v[i] := w     -- overwrites i-th slot in vector v.
              | SET_REFCELL                                                             # a := v
              | SET_REFCELL_TO_TAGGED_INT_VALUE                                         # a := v.       -- Tagged_Int-refcell stores are special because they don't need to be logged for the heapcleaner.
              | SET_EXCEPTION_HANDLER_REGISTER
              | SET_CURRENT_MICROTHREAD_REGISTER                                                # Dedicated 'register'. (Actually in ram on intel32.)
              | USELVAR
              | SET_STATE_OF_WEAK_POINTER_OR_SUSPENSION
              | FREE
              | ACCLINK
              | PSEUDOREG_SET
              | SETMARK
              | SET_NONHEAP_RAM  { kind_and_size: Number_Kind_And_Size }                                # Store into non-heap ram.
              | SET_NONHEAP_RAMSLOT  Type                                                       # v[i] := w     -- 64-bit writes for FLOAT64, 32-bit writes otherwise.
              ;

            # These fetch from the store, never
            # have functions as arguments:
            #
            Fetch_From_Ram
              = GET_REFCELL_CONTENTS
              | GET_VECSLOT_CONTENTS
              | GET_VECSLOT_NUMERIC_CONTENTS  { kind_and_size: Number_Kind_And_Size }
              | GET_STATE_OF_WEAK_POINTER_OR_SUSPENSION
              | DEFLVAR
              | GET_RUNTIME_ASM_PACKAGE_RECORD
              | GET_EXCEPTION_HANDLER_REGISTER
              | GET_CURRENT_MICROTHREAD_REGISTER
              | PSEUDOREG_GET
              | GET_FROM_NONHEAP_RAM  { kind_and_size: Number_Kind_And_Size }
              ;

            # These might raise exception exceptions, never
            # have functions as arguments:
            #
            Arith
              = ARITH  { op: Arithop, kind_and_size: Number_Kind_And_Size }
              | SHRINK_INT  (Int, Int)
              | SHRINK_UNT  (Int, Int)
              | SHRINK_INTEGER  Int
              | ROUND  { floor: Bool, from: Number_Kind_And_Size, to: Number_Kind_And_Size }
              ;

            # These don't raise exceptions
            # and don't access the store:
            #
            Pure
              = PURE_ARITH  { op: Arithop, kind_and_size: Number_Kind_And_Size }
              | PURE_GET_VECSLOT_NUMERIC_CONTENTS  { kind_and_size: Number_Kind_And_Size }
              | VECTOR_LENGTH_IN_SLOTS
              | HEAPCHUNK_LENGTH_IN_WORDS                                                               # Length excludes tagword itself.
              | MAKE_REFCELL
              | STRETCH  (Int, Int)
              | CHOP  (Int, Int)
              | COPY  (Int, Int)
              | STRETCH_TO_INTEGER Int
              | CHOP_INTEGER       Int
              | COPY_TO_INTEGER    Int
              | CONVERT_FLOAT  { from: Number_Kind_And_Size, to: Number_Kind_And_Size }
              | RO_VECTOR_GET
              | GET_BATAG_FROM_TAGWORD
              | MAKE_WEAK_POINTER_OR_SUSPENSION

              | WRAP
              | UNWRAP

              | CAST
              | GETCON
              | GETEXN

              | WRAP_FLOAT64            # Float
              | UNWRAP_FLOAT64          # Float

              | IWRAP           # Int
              | IUNWRAP         # Int

              | WRAP_INT1
              | UNWRAP_INT1

              | GETSEQDATA
              | RECORD_GET
              | RAW64_GET
              | MAKE_ZERO_LENGTH_VECTOR
              | ALLOT_RAW_RECORD            Null_Or( Record_Kind )                                      # Allocate uninitialized words from the heap.
              | CONDITIONAL_LOAD    Branch
              ;

            stipulate 

                fun ioper (GT : Compare_Op)  => (LE : Compare_Op);
                    ioper LE  => GT;
                    ioper LT  => GE; 
                    ioper GE  => LT;
                    ioper EQL => NEQ; 
                    ioper NEQ => EQL;
                end;

                fun foper f::EQ   => f::ULG;
                    foper f::ULG  => f::EQ;
                    foper f::GT   => f::ULE;
                    foper f::GE   => f::ULT;
                    foper f::LT   => f::UGE;
                    foper f::LE   => f::UGT;
                    foper f::LG   => f::UE;
                    foper f::LEG  => f::UN;
                    foper f::UGT  => f::LE;
                    foper f::UGE  => f::LT;
                    foper f::ULT  => f::GE;
                    foper f::ULE  => f::GT;
                    foper f::UE   => f::LG;
                    foper f::UN   => f::LEG;
                end;

            herein 

                fun opp IS_BOXED    => IS_UNBOXED; 
                    opp IS_UNBOXED  => IS_BOXED;
                    #
                    opp STRING_NEQ  => STRING_EQL; 
                    opp STRING_EQL  => STRING_NEQ;
                    #
                    opp POINTER_EQL => POINTER_NEQ; 
                    opp POINTER_NEQ => POINTER_EQL;
                    #
                    opp (COMPARE        { op, kind_and_size } ) =>  COMPARE             { op=>ioper op, kind_and_size };
                    opp (COMPARE_FLOATS { op, size     } ) =>  COMPARE_FLOATS   { op=>foper op, size     };
                end;
            end;

            iadd = ARITH { op => ADD,      kind_and_size=>INT 31 };
            isub = ARITH { op => SUBTRACT, kind_and_size=>INT 31 };
            imul = ARITH { op => MULTIPLY, kind_and_size=>INT 31 };
            idiv = ARITH { op => DIVIDE,   kind_and_size=>INT 31 };
            ineg = ARITH { op => NEGATE,   kind_and_size=>INT 31 };

            fadd = ARITH { op => ADD,      kind_and_size=>FLOAT 64 };
            fsub = ARITH { op => SUBTRACT, kind_and_size=>FLOAT 64 };
            fmul = ARITH { op => MULTIPLY, kind_and_size=>FLOAT 64 };
            fdiv = ARITH { op => DIVIDE,   kind_and_size=>FLOAT 64 };
            fneg = ARITH { op => NEGATE,   kind_and_size=>FLOAT 64 };

            ieql = COMPARE { op=>EQL, kind_and_size=>INT 31 };
            ineq = COMPARE { op=>NEQ, kind_and_size=>INT 31 };
            igt  = COMPARE { op=>GT,  kind_and_size=>INT 31 };
            ige  = COMPARE { op=>GE,  kind_and_size=>INT 31 };
            ile  = COMPARE { op=>LE,  kind_and_size=>INT 31 };
            ilt  = COMPARE { op=>LT,  kind_and_size=>INT 31 };
#           iltu = COMPARE { op=>LTU, kind_and_size=>INT 31 } 
#           igeu = COMPARE { op=>GEU, kind_and_size=>INT 31 }

            feql = COMPARE_FLOATS { op=>f::EQ, size=>64 };
            fneq = COMPARE_FLOATS { op=>f::LG, size=>64 };
            fgt  = COMPARE_FLOATS { op=>f::GT, size=>64 };
            fge  = COMPARE_FLOATS { op=>f::GE, size=>64 };
            fle  = COMPARE_FLOATS { op=>f::LE, size=>64 };
            flt  = COMPARE_FLOATS { op=>f::LT, size=>64 };

            fun arity NEGATE => 1;
                arity _      => 2;
            end;

        };                      # package p

        Codetemp = tmp::Codetemp;

        Value 
          = CODETEMP    Codetemp
          | LABEL       Codetemp
          | INT         Int
          | INT1        one_word_unt::Unt
          | FLOAT64     String
          | STRING      String
          | CHUNK       unsafe::unsafe_chunk::Chunk
          | TRUEVOID
          ;

        Fieldpath                                                                       # How do we access the value of a given RECORD slot?
          = SLOT                Int                                                     # Directly, as slot six or whatever.
          | VIA_SLOT            (Int, Fieldpath)                                        # Indirectly through a series of fetches, starting with slot six or whatever.
          ;

        # See copious comments in:  src/lib/compiler/back/top/nextcode/nextcode-form.api
        #
        Callers_Info
          = FATE_FN                                                                     # Fate ("continuation") functions. Fate functions are never recursive; there is at most one per ncf::DEFINE_FUNS.
          | PRIVATE_FN                                                                  # A fun is 'private' if we known all possible callers -- this lets us optimize the calling register conventions for it.
          | PRIVATE_RECURSIVE_FN                                                        # Private recursive functions.
          | PRIVATE_FN_WHICH_NEEDS_HEAPLIMIT_CHECK                                      # Private functions that need a heap limit check.
          | PRIVATE_TAIL_RECURSIVE_FN                                                   # Private tail-recursive kernel functions.
          | PRIVATE_FATE_FN                                                             # Private fate ("continuation") functions.
          | PUBLIC_FN                                                                   # Before the closure phase: any user function; After  the closure phase: Any externally visible fun. (=> requires std call protocol.)
          | NO_INLINE_INTO
          ;

        Instruction                                                                     # One or more instructions chained through 'next'.
          #
          = DEFINE_RECORD                                                               # Create a 'kind' record with 'fields', store it in 'to_temp', then execute 'next'.
              { kind:           Record_Kind,                                            # record / fate / ... 
                fields:         List( (Value, Fieldpath) ),
                to_temp:        Codetemp,
                next:           Instruction                                             # Next instruction to execute.
              }
          | GET_FIELD_I                                                                 # Store field 'i' of 'record'  in ('to_temp': 'type'), then execute 'next'.
              { i:              Int,
                record:         Value,
                to_temp:        Codetemp,
                type:           Type,
                next:           Instruction                                             # Next instruction to execute.
              }
          | GET_ADDRESS_OF_FIELD_I                                                      # Store address of field 'i' of 'record' in ('to_temp': 'type'), then execute 'next'.
              { i:              Int,
                record:         Value,
                to_temp:        Codetemp,
                next:           Instruction                                             # Next instruction to execute.
              }

          | TAIL_CALL                                                                   # Apply 'fn' to 'args'. Nextcode fns don't return so there is no 'next' field -- this is essentially a "jump with arguments".
              {
                fn:             Value,
                args:           List(Value)
              }

          | DEFINE_FUNS                                                                 # Define 'funs', then execute 'next'. Often a single fun is defined, but potentially a set of mutually recursive fns.
              {
                funs:           List(Function),
                next:           Instruction
              }

          | JUMPTABLE                                                                   # Evaluate i-th of N nexts. xvar is used for def/use accounting -- created fresh at start of nextcode, discarded at end.
              {
                i:              Value,
                xvar:           Codetemp,
                nexts:          List(Instruction)
              }

          | IF_THEN_ELSE                                                                # If 'op'('args') do 'then_next' else 'else_next'.
              { op:             p::Branch,                                              # Specifies comparison (GT, LE...), bit resolution etc.
                args:           List(Value),
                xvar:           Codetemp,                                               # xvar is for branch-probability estimation via def/use accounting -- created at start of nextcode, discarded at end.
                then_next:      Instruction,                                            # Next instruction to execute if condition is TRUE.
                else_next:      Instruction                                             # Next instruction to execute if condition is FALSE.
              }

          | STORE_TO_RAM
              { op:             p::Store_To_Ram,                                        # Are we storing into a refcell, rw_vector, or something weird?  Are we storing a pointer or an immediate value?
                args:           List(Value),                                            # Actual value to store.
                next:           Instruction                                             # Next instruction to execute.
              }

          | FETCH_FROM_RAM                                                              # Store 'op'('args') in ('to_temp': 'type'), then execute 'next'.  Our 'op' never has functions as arguments.
              { op:             p::Fetch_From_Ram,                                      # Are we fetching from a refcell, rw_vector, globally allocated register...?
                args:           List(Value),                                            # Typically [v,i] if we're fetching v[i] -- depends on 'op'.
                to_temp:        Codetemp,                                               # We publish fetch result under this name during execution of 'fate'.
                type:           Type,                                                   # We publish fetch result under this type during execution of 'fate'.
                next:           Instruction                                             # Next instruction to execute.
              }

          | ARITH                                                                       # Store 'op'('args') in ('to_temp': 'type'), then execute of 'next'.
              { op:             p::Arith,
                args:           List(Value),
                to_temp:        Codetemp,
                type:           Type,
                next:           Instruction                                             # Next instruction to execute.
              }

          | PURE                                                                        # Store 'op'('args') in ('to_temp': 'type'), then execute of 'next'.
              { op:             p::Pure,
                args:           List(Value),
                to_temp:        Codetemp,
                type:           Type,
                next:           Instruction                                             # Next instruction to execute.
              }

          | RAW_C_CALL                                                                  # Invoke C function 'linkage' with 'args', publish return values as 'results' during execution of 'fate'.
              {
                kind:           Rcc_Kind,
                cfun_name:      String,
                cfun_type:      cty::Cfun_Type,                                         # Either "" or else linkage info as   "shared_library_name/name_of_the_C_function".
                args:           List(Value),
                to_ttemps:      List( (Codetemp, Type) ),                               # Like 'to_temp' above, but a list of (Codetemp,Type) pairs instead of a single Codetemp.
                next:           Instruction                                             # Next instruction to execute.
              }
                #
                # Experimental "raw C call" (Blume, 1/2001) -- see comments in   src/lib/compiler/back/top/nextcode/nextcode-form.api

        also
        Rcc_Kind
            =
            FAST_RCC | REENTRANT_RCC

        withtype
            Function
                =
                ( Callers_Info,                                                         # E.g., if all callers are known, we can construct a custom calling convention for better time and space performance.
                  Codetemp,
                  List( Codetemp ),
                  List( Type ),
                  Instruction
                );

        fun has_raw_c_call  cexp
            =
            case cexp
                #                 
                RAW_C_CALL _                      =>  TRUE;
                TAIL_CALL  _                      =>  FALSE;
                #
                DEFINE_RECORD           { next, ... }     =>  has_raw_c_call  next;
                GET_FIELD_I             { next, ... }     =>  has_raw_c_call  next;
                GET_ADDRESS_OF_FIELD_I  { next, ... }     =>  has_raw_c_call  next;
                STORE_TO_RAM            { next, ... }     =>  has_raw_c_call  next;
                FETCH_FROM_RAM          { next, ... }     =>  has_raw_c_call  next;
                ARITH                   { next, ... }     =>  has_raw_c_call  next;
                PURE                    { next, ... }     =>  has_raw_c_call  next;
                #
                IF_THEN_ELSE            { then_next, else_next, ... } =>  has_raw_c_call  then_next
                                                                      or  has_raw_c_call  else_next;
                #
                JUMPTABLE               { nexts, ... }    =>  check_list  nexts;
                #
                DEFINE_FUNS { funs, next }
                    =>
                    has_raw_c_call  next
                    or
                    check_list
                        (map  (\\ (_, _, _, _, e) = e)  funs);
            esac
            where
                fun check_list (c ! rest) =>  has_raw_c_call (c) or check_list (rest);
                    check_list []         =>  FALSE;
                end;
            end;

        fun size_in_bits   typ::FLOAT64 =>  64; 
            size_in_bits ( typ::INT
                         | typ::INT1
                         | typ::POINTER _
                         | typ::FUN
                         | typ::FATE
                         | typ::DSP
                         ) => 32;                               # 64-bit issue XXX BUGGO FIXME
        end;

        fun is_float   typ::FLOAT64         =>  TRUE;
            is_float ( typ::INT
                     | typ::INT1
                     | typ::POINTER _
                     | typ::FUN
                     | typ::FATE
                     | typ::DSP
                     )                      => FALSE;
        end;

        fun is_tagged ( typ::FLOAT64
                      | typ::INT1
                      )                          =>  FALSE;
            is_tagged ( typ::INT
                      | typ::POINTER _
                      | typ::FUN
                      | typ::FATE
                      | typ::DSP
                      )                          =>  TRUE;
        end;

        fun cty_to_string  typ::INT              =>  "[I]";
            cty_to_string  typ::INT1             =>  "[I32]";
            cty_to_string  typ::FLOAT64          =>  "[R]";
            cty_to_string (typ::POINTER (RPT k)) =>  ("[PR" + (int::to_string (k)) + "]");
            cty_to_string (typ::POINTER (FPT k)) =>  ("[PF" + (int::to_string (k)) + "]");
            cty_to_string (typ::POINTER  VPT )   =>  "[PV]";
            cty_to_string  typ::FUN              =>  "[F]";
            cty_to_string  typ::FATE             =>  "[C]";
            cty_to_string  typ::DSP              =>  "[D]";
        end;

        fun combinepaths (p, SLOT 0)
                =>
                p;

            combinepaths (p, q)
                => 
                comb p
                where
                    recursive my comb
                        =
                        \\ (SLOT 0)
                               =>
                               q;

                           (SLOT i)
                               =>
                               case q   
                                   (SLOT j) => SLOT (i+j);
                                   (VIA_SLOT (j, p)) => VIA_SLOT (i+j, p);
                               esac;

                           (VIA_SLOT (i, p))
                              =>
                              VIA_SLOT (i, comb p);
                        end;
                end;
        end;

        fun lenp (SLOT _) => 0;
            lenp (VIA_SLOT(_, p)) => 1 + lenp p;
        end;

        bogus_pointer_type = typ::POINTER VPT;                          # Bogus pointer type whose length is unknown 

        stipulate
            package hcf = highcode_form;                                # highcode_form         is from   src/lib/compiler/back/top/highcode/highcode-form.pkg
            #
            tc_float64 =  hcf::float64_uniqtype;
            lt_float64 =  hcf::float64_uniqtypoid;
        herein

            fun tcflt tc = if (hcf::same_uniqtype      (tc, tc_float64))  TRUE; else FALSE; fi;
            fun ltflt lt = if (hcf::same_uniqtypoid (lt, lt_float64))  TRUE; else FALSE; fi;

            fun rtyc (f, []) => RPT 0;

                rtyc (f, ts)
                    =>
                    loop (ts, TRUE, 0)
                    where
                        fun loop (a ! r, b, len)
                                => 
                                if (f a)   loop (r,     b, len+1);
                                else       loop (r, FALSE, len+1);
                                fi;

                            loop ([], b, len)
                                =>
                                if b   FPT len;
                                else   RPT len;
                                fi;
                        end;  
                    end;
            end;

            fun uniqtype_to_nextcode tc
                =
                hcf::if_uniqtype_is_basetype (

                   tc, 

                   \\ pt =   if   (pt == hbt::basetype_tagged_int) typ::INT;
                             elif (pt == hbt::basetype_int1)       typ::INT1;
                             elif (pt == hbt::basetype_float64)    typ::FLOAT64;
                             else                                  bogus_pointer_type;
                             fi,

                   \\ tc
                       =
                       hcf::if_uniqtype_is_tuple (
                           tc,
                           \\ ts =  typ::POINTER (rtyc (tcflt, ts)),

                           \\ tc = if   (hcf::uniqtype_is_arrow tc)  typ::FUN;
                                   elif (hcf::uniqtype_is_fate  tc)  typ::FATE;
                                   else                             bogus_pointer_type;
                                   fi
                       )
               );

            fun uniqtypoid_to_nextcode_type lt
                = 
                hcf::if_uniqtypoid_is_type (

                    lt,

                    \\ tc =  uniqtype_to_nextcode tc,

                    \\ lt =  hcf::if_uniqtypoid_is_package (

                                 lt,

                                 \\ ts =  typ::POINTER (rtyc (\\ _ = FALSE, ts)), 

                                 \\ lt =  if   (hcf::uniqtypoid_is_generic_package  lt)   typ::FUN;
                                          elif (hcf::uniqtypoid_is_fate lt)               typ::FATE;
                                          else                                          bogus_pointer_type;
                                          fi
                             )
                );

        end;            # stipulate
    };                  # package nextcode
end;                    # stipulate



Comments and suggestions to: bugs@mythryl.org

PreviousUpNext