2012-08-18

one call for trees of operations

7.22: addm/one call for trees of operations:

. the key to efficiency will be
providing support for integrated use of
both oop and concrete types;
ie, if the tree needs to be done fast
or in a tight space,
then the compiler uses native types;
but if it needs to handle polymorphism,
then it uses the type-tags,
and sends it to an oop type .

biop* oop:
*: (binary operations)
. the problem with the popular oop model
is that it is not helping much for biop's;
so, we should consider the binary parameter
to be one obj;
and, we again have oop working .
[8.13:
. this has always been implicit in my design;
biop oop works by featuring
2 type tags per arg: (supertype, subtype);
eg, (number, integer);
and then we don't have to worry about
where to send a (float, integer)
because they both have
the same supertype: number .
. this note was just pointing out
that I was realizing the syntax x`f -- vs f(x) --
was ok; whereas, previously
I had howled that oop was absurd because
it turned (x * y) into x`*(y)
as shorthand for asking x's type mgt
to apply the *-operation to (x,y);
what oop needs to be doing is (x,y)`*
as a shorthand for calling the type mgt that is
the nearest supertype shared by both x and y,
and asking it to apply the *-operation to (x,y).
8.15: and of coure,
oop langs like c++ need to get their own lang
and stop trying to fit within C,
so then we can go back to (x*y)
as a way to write (x,y)`* .]
feeding entire expression trees to type mgt:
. whenever we have a tree of operations,
all of which are handled by the same supertype;
(eg, d`= x+y*z are all handled by Number.type)
then we want to make just one call to that type's mgt
for handling the whole tree .
[mis:]
. if any of the tree's inputs are a pointer to
data that is not local to the caller,
it needs a type.tag indicating that its address
is just a dopevector to a full ptr .
[8.3: background:
. the model I had in mind during that note
was that the references in the tree
were saving space by merely being offsets of
a base pointer that found the caller's symbol table;
that note was pointing out that
if the operand is not local to the caller
then it won't be in the caller's symbol table
so the operand's reference to its value
can't be interpreted in the usual way;
however, that assumption is obsolete:
. the new model sees all visible obj's
-- including the non-locals --
as having an entry in the local symbol table;
because, the symbol table is where the metadata is,
and we need metadata on all visibles,
not just all locals .
. typical metadata might look like this:
( locality.{current, parent, gparent, ..., global}
, process id: (zero if local to this process)
, offset: value's location
-- an offset from locality's stack pointer
[8.15: if negative it means value is in a register
(the first few words of the act'rec
are reserved for saving registers).]
, supertype.tag: sys'pointer to type mgt,
   -- or null if a varying type,
   -- to indicate that tag is with value .
, subtype.tag: -- max-int if not static
    -- to indicate that tag is with value .
, etc
) . next, we consider multi-threading:
. if it's a normal (synchrounous) call
then after making a call,
the caller is getting suspended;
so, we can say the caller's locals are
really owned by the called service's thread .
. if the call is asynchrounous
(where the service just takes the order,
returns immediately, and returns the result later)
then the called service needs to:
# make copies of in-mode parameters
# set the lock on out-mode parameters .
. the lock tells the caller
that this local is owned by the thread of
one of the called services .
. finally, when the service is done,
and has provided the promised output,
it unlocks the affected vars .]

8.13: addm/how does locking work?:
. locking typically happens for promises:
an asynchronous call returns immediately
by promising to provide the output later;
and until then,
the output targets will be locked .
. this can be done automatically like so:
whenever the compiler finds that a var
is targeted by an asynchronous call,
it implicitely declares it as lockable
which is a sort of pointer type
that is set to null when locked .
. if a process tries to use a locked var
then it obviously has nothing better to do
until this var has been defined;
therefore, such a null access
causes the process to be suspended
until the var is found to be non-null again .
. if a process does have better things to do,
we will want it to
explicitely declare the var as being lockable,
and then check for null before each use of it .

oop types as virtual coprocessors:

. each oop type can be serviced by a
separate virtual processor
.  how is it that having separate processors
can help the addm virtual machine
if it's no longer used in physical designs?
(even graphics are integrated now).
. modularity eases maintenance and troubleshooting; [8.15:
and, when dealing with software,
small is good to avoid cache misses .]

. how do we deal with cross-module
communication inefficiencies?
review the required communications:
why would ipc necessarily be inefficient?
(the modularity could be virtual,
like it is in Singularity:
instead of copying data between processors,
they trade locks on objects
so they need copy only pointers).

. thanks to compiler checking,
the expression tree's in-mode parameters
can be trusted to be read-only
so we have modularity there,
without having to trade ownership tokens .
. we want to send inputs as pointers
even for small objects; because,
type mgt might need to recopy them anyway
to load the inputs into registers,
and arrange type.tags in a consistent way .
[mis:]
. if all the expr'tree obj's are local to caller
then they're in the same subheap;
and, this can reduce their pointer size .
. the tree being sent is a pointer to
a special structure that includes
a pointer to the caller's subheap,
and all the tree's obj's are offsets of that
base pointer to subheap .
[8.15:
. that note is obsolete in the new model:
all inputs and addresses are references to the
caller's symbol table entries
in order to give type mgt access to metadata .]

. maybe we can save space on type.tags
by having one tag for an entire expr'tree?
but what are the chances that would be needed?
. if they are going to be the
same subtype all the time,
then just statically send the tree to
the appropriate subtype's mgt;
eg, if they are all rationals,
then instead of sending it to Number.type,
we would send it to Rational.type .
[mis:]
. how can obj`type.tags be implicit?
they can't:
that only makes sense for native operations,
not for operations handled by a type mgt .
. when checking that a param has the right type,
the value can come in without a type.tag,
[ and then the type.tag is part of the
static meta.data in the symbol table .]
and when we need operations on that value
we call that particular static type .
. but when dealing with our biop oop
-- dynamically varying types --
then the type mgt is not expecting a particular type,
it's expecting a pair of type-tagged values .
[8.15:
. but we might also have a mixing of static types
eg an operation on (always int, always float)
or we could have mixes of category:
ie, (static type.tag, dynamic type.tagging);
so, we do support implicit tags,
like this:
. the references being passed to type mgt
are pointing at the symbol table nodes,
not refering to the stacked objects directly;
the symbol nodes hold the meta.data,
that includes:
# a reference to stack or register,
# what the supertype is,
# whether the subtype is on the symbol node
or with the object's value as a type.tag .]