The Quirks architecture don’t look like a real processors architecture. Nevertheless it consists of a set of functional units which can be mapped on units of real processors.
First of all there are no conception of general purpose registers. In other words, there is no fixed set of cells with fixed structure marked out separately. There are no specific operations for particular cells. In the Quirks all cells of computer memory have equal rights.
Let’s see the example. Let memory has the following structure: ‘st1 .= <uint8 arr *>’ (it will be explained a few pages later), i.e. a sequence of bytes. The UVM command
st[1505] <- st[1500];
moves a byte from the cell 1500 to the cell 1505.
The second big difference is an absence of predefined arithmetic and
logical operations. Instead of this there is a extendable set of
arithmetic modules defined by input/output structure and microprogram
(from a VM point of view). Such microprogram is native implemetation of
a user defined operation or rule how JIT should generate native
code. The set of such modules is grouped into cartridges. Beside
of standard cartridge (with base level operation such as add
,
mul
etc.) there can be diferent additional cartridges with more
specific operations or operations on special types (for example, matrix
arithmetic or string manipulations). There can be processor-oriented
cartridges which describe how to implement particular arithmetic action
on particular data type with this particular processor by the most
effective way.
As was mentioned above all memory cells are registers of
UVM (to say it more accurately, only that memory which is passed to
the UVM by an environment; the role of the environment plays a program
which links UVM). ‘st’ - it is what usually named "memory". The
environment can pass several continuous blocks of memory to the UVM -
st1
, st2
, ... (each one is specified by a pointer and a
size). It is unable for the UVM to use memory out of boundaries of these
blocks. st1
, st2
, ... are named selectors - they
are used as handles of that memory1. ‘st’ denotes "static memory".
Besides, the UVM can allocate its own memory blocks. Some amount of
memory can be allocated each time the UVM enters a procedure (and
deallocated when it leaves the procedure). It is memory for local
variables. It is named ‘lv’ (local
variables) and can be splitted to pieces by selectors
lv1
, lv2
, ... (we need more than one for some languages
which allow blocks with dynamic structures inside procedure, for
example Ada). You can access all local variables of the current
procedure by those selectors (but only variables of the current
procedure!). All lv-selectors names are procedure scoped.
Some amount of memory can be allocated dynamically (as with
malloc
in C) in a procedure. It is designated as ‘dn’. It
can be several dn-selectors as with ‘st’, the only difference is
that st-selectors is globally scoped but all dn-selectors use a
procedure as a name space.
ct-memory is very similar to ‘st’. It is given by an environment also, it can be represented by several selectors and it is globally scoped also. The difference that for ‘ct’ an environment guarantee immutability all the time the UVM works2. It allows JIT to substitute the values from ‘ct’ as immediate operands. For example, the command
lv[0] <- ct[0];
can be translated by JIT compiler for ‘x86’ as follows:
mov eax,10
(if the containment of ‘ct[0]’ is 10
and ‘lv[0]’ is
mapped on eax
).
The memory types ‘fp’, ‘dx’, ‘dy’ are named pseudo memory. All the cells of that memory is references to some cells in ‘lv’, ‘st’, ‘ct’, ‘dn’ which are really exists.
An ‘fp’ has only one selector - fp
. It is parameters and
return values frame of a procedure3. This approach allows don’t use temporary values for some
arguments when we build a native code and pass them as a direct
reference to a memory or as a register.
‘dx’ and ‘dy’ is used as a general purpose referenced
memory. Each type has its own selector set: dx1
, dx2
,
... and dy1
, dy2
, ... . All selector names are
procedure scoped. That memory is used, for example, for passing an
arguments frame to a procedure.
The ‘au’ memory will be discussed later.
Procedure call and return consists of the following steps:
In contrast to ordinary (hardware) processors any memory cell
(‘ct’, ‘st’, ‘dn’, ‘fp’, ‘dx’, ‘dy’,
‘au’, ‘lv’) can have any complexity (from one bit to
practically any data structure). Moreover, the structure of a concrete
cell can be unknown and passed by an environment. In any case it will be
represented by a descriptor. Descriptors can have a tree
structure and describe complex types with more simple
ones. Atomic types play the role of leafs. Nodes are constructed
with complex organizers such as stu
(a structure in C), arr
(an array) etc.
For example, an array of 40 2-element records can be described as follows:
<<int8 int32 stu> arr 0..39>
A descriptor can organize any type of UVM memory (operations also)
and it bounds to a selector at the time the selector appears. (No one
can change this binding later). An access to a cell of memory is
performed according to the descriptor. Suppose, some selector st1
which is tuned by the above descriptor will be passed by an
environment. A code can refer to that memory cells such way:
st1 - the whole array (40 elements) st1[4] - 5th element of the array (the 2-element structure) st1[5:0] - the first element of the structure in the 4th array element
A sequence of numbers which is used for cell access (for example, ‘5:0’) is named order. In the example above an order depth can be any from the range 0 .. 2. When one try get access to a cell which is not mentioned in the descriptor the exception is raised.
For example:
st[40] - exception! (no such cell) st[0:0:0] - exception! (order too deep)
Thus, if a procedure get a parameter frame (fp
) than the
procedure can’t get access to anything else beside fp
and
globally visible selectors (‘st’ and ‘ct’).
An arithmetic units is a piquant of Quirks.
Each AU is a structure and as any other structure in Quirks
it has a descriptor. All kinds of AU structures can be accessed
through au-pseudo memory. But structure is not only thing that AU
has. Each AU performs some operation. For example, the adder for
32-bit integers can have a structure <int32 int32 int32 stu>
, so
it can be accessed through three cells. The first and second cells are
items, the third is a sum. The adder cells are mapped onto other
memory (or AU) cells exactly as any other pseudo memory (‘dx’,
‘dy’) can be mapped. See Mapping in Quirks UVM architecture overview.
The adder function is to sum first two cells and place the result into the third4.
Adders (and another modules, but now the story about adders) can operate with more complex cells, for example with vectors. Here is an example of some simple adder which sums vectors of bytes:
<(int32 int32) <int8 arr *1..*2> <int8 arr *1..*2> <int8 arr *1..*2> stu>
The elements here are dynamic arrays. The construction ‘(int32
int32)’ defines discriminant cells. The first cell designates the
bottom array index (*1
), the second designates the top
(*2
). Thus, it is a adder of variable size vectors (but all
three components should have the same size - the descriptor says exactly
it).
Let’s imagine a procedure which sums two numbers:
procedure :Sum is declare map auSum is fp descriptor; map is (auSum[0], fp[0]) (auSum[1], fp[1]) (auSum[2], fp[2]) end map; begin * auSum; end :Sum;
auSum
is the first adder (as for any other memory can be
several selectors, but now we have several ones for the same module also:
au1Sum
= auSum
, au2Sum
, au3Sum
, ... - all
names are procedure scoped).
This procedure is generic (any procedure in Quirks is generic. See The model of a procedure in Quirks UVM architecture overview.) The used adder is designated by a parameter frame descriptor.
In a very compressed form the structure and roles of 4 Quirks UVM modules were examined here. It is a start knowledge which should help to understand our benchmark results and others more detailed documents as well.
The topics such as:
were avoided here. See Quirks UVM architecture in Quirks UVM architecture. See Quirks UVM command set in Quirks UVM command set.
The first selector for every memory type can be designated without the
number (i.e. st1
equal to st
etc.).
It is not exactly reflect the situation. See The model of a procedure in Quirks UVM architecture overview.
It can be used for access to local variables of a procedure which encloses the current procedure, as in Pascal.
Really, any adder in Quirks can perform as a minimum 3 operations: cell 3 = cell 1 + cell 2, cell 1 = cell 3 - cell 2 and cell 2 = cell 3 - cell 1. It depends on the cell modes. See Arithmetic modules and a mapping in Quirks UVM architecture overview.