A zero-address instruction has no explicit operand addresses, so its operands and result location must be implicit in the instruction set architecture. A stack machine supplies these naturally: operations remove their inputs from the top of an operand stack and push the result. A fixed-processor register design is logically possible, so “almost certainly” is appropriately weaker than a proof that only a stack machine could work.
Write the stack from bottom to top, with its top on the right. Let stand for any unchanged prefix. Suitable effects are
Thus add pops two operands and pushes one sum, reducing stack occupancy by one. The special const c instruction pushes its immediate constant and increases occupancy by one. Only const needs an explicit value in its encoding.
Use two zero-address instructions for indirect access to computer memory. Define load to replace the top memory address by the value at that address, and define store to pop a value followed by the address just beneath it:
Both have no encoded address operand; the stack supplies it. If the known fixed memory addresses of the variables are , implement the assignment with
const p_x
const p_y
load
store
The successive stack suffixes are , then , then , then empty. Hence
The surrounding stack is restored. Other store conventions are possible, but the operand order must be stated and used consistently.
A computed memory address is just another integer on the operand stack. For a pointer held at fixed address , const p; load obtains the pointed-to address; another load obtains its contents. More generally, any expression that leaves an address on top may be followed by load, or may be retained beneath a computed value for store.
For an array whose base address is and whose element size is address units, the address of entry is , where stores the index. Add zero-address instructions mul and dup, with dup mapping to . A suitable sequence is
const b
const p_i
load
const w
mul
add
dup
load
const 1
add
store
The first six instructions leave the element address . The remaining suffix evolves as
with the final effect
The address is computed once and duplicated, so both accesses refer to the same array element. For unit-sized elements omit const w; mul. If the base address is itself held in a variable, replace const b by the instructions loading that variable's value. No instruction other than const requires an explicit operand.
Let and measure the extra operand stack space required from the current base, with each evaluation leaving one result. Evaluating first uses slots. Its result occupies one slot while evaluating , so that stage uses slots. The final add reduces two slots to one. Thus
By commutativity of addition, choose the smaller of these two bounds. Evaluate the child of larger depth first. The optimum recurrence on a binary expression tree is
Assign depth one to a literal or a simple already-addressable value, and compute the child depths recursively. If , the depths are integers, so ; evaluating first therefore attains , while reversing the order needs . If the depths are equal, whichever child goes second needs the extra retained-result slot. These lower bounds prove the recurrence is optimal for evaluating each child subtree without interleaving or spilling. Apply it at every node to minimize peak operand stack occupancy for the given binary expression tree; this is optimal stack evaluation of a binary expression tree.
This changes child evaluation order, not the parenthesization. For example, a chain-shaped binary expression tree can have depth two, whereas a balanced tree of eight leaves has depth four under this model. The reordering assumes expressions have no order-sensitive side effects or observable exceptions. Reassociating floating-point addition would require additional care because it can change floating-point rounding.
Fix sub to mean . If the depth-minimizing order evaluates the right operand before the left operand , the stack ends in , so ordinary sub would compute the wrong sign. Two possible instruction set architecture extensions are:
  • Add swap, with . After computing and then , use swap; sub to obtain . One general zero-address instruction handles every noncommutative binary operation and is useful elsewhere, but each reversed evaluation needs an extra instruction and extra execution time. It can be implemented without an additional operand stack slot.
  • Add reverse-operand variants, such as rsub with , and analogously reverse division and other required operations. After computing and then , a single rsub obtains . This avoids the extra executed swap, but requires more opcodes and decoder support, usually one reverse form for each affected operation.
Both preserve the same peak operand stack bound:
The first trades extra dynamic instructions for a smaller instruction set; the second trades a larger instruction set for fewer executed instructions. Neither extension makes order-sensitive side effects safe to reorder.

Articles by others on the same topic (0)

There are currently no matching articles.