On Thursday, January 26, 2017 at 4:30:42 PM UTC-5, Rick C. Hodgin wrote:
> Does C have any kind of syntax which allows code to be constructed that
> can be used for generic purposes, but without following the standard
> prologue and epilogue code through a function?
>
> I'm thinking I want to design a block of code like this:
>
> block xyz
> {
> int a, c;
>
> input b; // By block {..} protocol, uses an input register
>
> a = myfunc1();
> c = a + b;
>
> output c; // By block {..} protocol, uses an output register
> }
>
> [snip]
>
> In the following example, I am able to take a defined block and
> copy it into another function so as to prevent the dispatch of
> the callback function call:
>
> block my_qsort_compare
> {
> input left as SStruct1*, right as SStruct1*;
>
> if (left->value > right->value) result = 1;
> else if (left->value < right->value) result = -1;
> else result = 0;
>
> output result;
> }
>
> Then in a modified qsort_block() version, I could provide parameters:
>
> qsort_block(&data[0], count, data_size,
> my_qsort_compare, sizeof(my_qsort_compare));
>
> The qsort_block() function copies in the block of code so that it
> doesn't have to issue function call dispatches, but inlines the
> logic through some kind of established input and output protocols
> where parameters enter and exit knowingly without going through
> the stack, but reference wherever they already are in the specific
> instance use.
>
> Is there a way to do something like this in C?
I've had an extended idea about this. I've had the idea as above
to be implemented in hardware so that each thread can make in-flight,
at runtime corrections to local source code, allowing overlays to be
used which optimize published algorithms based on the dynamics of the
runtime environment.
That will all work fine and it is planned for my Arxoda CPU design.
The extended idea I've had is to take the concept of creating each
algorithm back a step, and leave it at a point of purposefully in-
complete compilation (even in standard library form) so that the
final stages of compilation can be completed at runtime, with a nod
given to instance loads.
The idea is to have an algorithm broken out into pre-compiled template
components. The algorithm is fully coded, but it is missing key or
crucial sections which require the unique features of the runtime need
to fill in those spots with the algorithms which will make them run
optimally for their need.
In this way, at compile time the code is compiled to a series of parts.
Each part is then stored in the binary or library. The compiler adds
startup code which then assembles those parts into a final form based
on the dynamic needs of the runtime environment, even doing so in a
lazy form, such as when an unassembled algorithm is called the first
time, it then assembles itself into an instance for that call. That
instance then persists so that future references to it are dispatched
properly without the later need for assembly.
By creating the compiled code portions of an algorithm in steps, with
only the key missing components put into place dynamically at runtime,
standard dynamic link libraries can be constructed which are able to
be optimized with the local code at runtime, with only a minimal hit
on load, or on first use.
I'll create a simple example of this at some point and publish it.
But in your mind, think of qsort(). It requires logic to navigate
the list. That logic does not change. But, the testing criteria
within does change. So, rather than creating a single qsort()
algorithm with a callback, or creating a qsort_block() algorithm
with padding to allow the insertion of code, create a qsort_un()
algorithm which is unassembled, which contains pointers to the
various parts which can be assembled. Then, in the source code
which references qsort_un() it has instance information based on
that which needs to be injected (as provided for by the IDE + CAlive
compiler, which informs the user of what's needed to make it work,
as by published specs of qsort_un().
In code it might be used like this:
qsort_un(data, count, size) {
connect left as int*;
connect right as int*;
return (*left < *right ? -1 : (*left > *right ? : 1 : 0));
}
Then at runtime, this code was also pre-compiled, and can then be
linked to the qsort_un() algorithm, inserting that block to all of
the connects and returns to the runtime instance. The compiler
could even have left the register selection in flux so that it
could be determined based on the runtime instance needs of the
algorithm, so it could be better optimized right then.
I see real potential for this idea in terms of breaking down algorithms
to more than mere functions or exportable stable/solid components.
We can now have algorithms broken out into fundamental blocks, able
to be assembled dynamically at runtime, assembled into a more optimized
form for the runtime needs of that particular instance of the app,
based on the particular data it's processing (perhaps the particular
qsort_un() block above was never traversed, it would never have been
assembled in a lazy model, so only the other qsort_un() blocks would've
been created. And so on.
In any event, I think I'm going to incorporate this ability into
CAlive. I'll probably call it "incoding" (meaning it's in the
"process of being coded, but is not yet complete").
An incoded algorithm can be published (like qsort_un()), which allows
other algorithms to incode themselves to that reference during editing,
so they are compiled with that known reference, their connects, and
returns (plural), so that they are all connected at runtime to create
the final form.
This will move a small portion of the compiler and linker into the
executable. However, with a proper binary loader for this new ABI,
that portion will not be in the executable, just a small table of
information the loader will read which then directs the connecting
process at launch, and also at runtime for lazy models.