ARM architecture family 2026-10-24
A family of processor architectures with several execution states and instruction encodings. The 32-bit instruction sets include the compact Thumb instruction set; the 32-bit AAPCS32 calling convention specifies interoperation at subroutine boundaries.
The regular University of Oxford preliminary examination was held on 20 June 2019, from 14:30 to 17:30. These solutions cover every marked part of all eight questions: digital systems in Questions 1–3, linear algebra in Questions 4–6, and natural deduction in Questions 7–8, including questions beyond the examination's permitted choice. The supplied Thumb instruction set reference and primitive proof rules are part of the held paper.
Source release: Oxford Computer Science examination archive, member 2018/T/a10101w1.pdf. The native cover establishes the 2019 regular sitting despite the archive's 2018/T directory. The printed code is A10101W1. A correction to a printed invariant bound is explained in Question 1(e); all inputs needed for the remaining solutions are present.
Use four working processor registers by packing the growing quotient into the vacated low bits of the shifting dividend. The following is A32 assembly language with conditional execution, an allowed instance of the question's general 32-bit Reduced instruction set computer. It is not restricted to the sixteen-bit encodings in the supplied Thumb instruction set table. On entry r0 is the dividend and r1 the positive divisor; r2 becomes the high remainder machine word and r3 counts iterations. The quotient is returned in r0 and the ordinary remainder remains in r2.
mov   r2, #0
mov   r3, #32
loop:
adds  r0, r0, r0
adcs  r2, r2, r2
cmp   r2, r1
subcs r2, r2, r1
adc   r0, r0, #0
subs  r3, r3, #1
bne   loop
bx    lr
Here subcs means conditional sub when arithmetic carry is set, without the flag-setting suffix. adc also preserves flags. Consequently the arithmetic carry from cmp survives until it is inserted as the next quotient bit. The loop has seven instructions, one of them conditional, and no data-dependent branch. Its sole branch is the backwards loop branch, taken 31 times. It uses no separate quotient processor register, zero processor register, temporary comparison processor register or saved arithmetic carry processor register. A Thumb instruction set rendering using conditional execution would need an explicit conditional-execution encoding such as it cs; that is an additional encoded instruction, not an unexplained free operation.
The packed restoring division loop invariant relates it to the displayed algorithm. Specifically, let , let retain its original mathematical value, and let be the number of completed iterations. Define the processed prefix , its quotient and remainder . The processor register loop invariant is
The two terms in the low machine word occupy disjoint bit positions. Its next outgoing arithmetic carry is therefore the next original dividend bit, not a quotient bit. adds shifts the low machine word; adcs doubles the high remainder and incorporates that dividend bit. cmp decides whether this trial remainder is at least the divisor; subcs performs the conditional subtraction, and adc appends its decision to the shifted quotient. This is exactly the doubling, compare/subtract and quotient-bit update, with the counter maintained separately. Before processing bit , the trial remainder is at most , so the high-word addition does not require a hidden 33rd remainder bit for this 32-bit dividend problem. After 32 iterations the residual dividend portion is zero, giving the stated outputs.
There is a printed source error in the auxiliary bound. At the initial state, choosing valid inputs makes the printed strict inequality false. The algorithm instead preserves for its unpacked 64-bit variable, together with its printed equality loop invariant. Doubling and subtracting when necessary preserves that corrected bound. At termination the equality becomes ; since is then divisible by , it gives . This source error does not invalidate the algorithm or the packed implementation. The figure is mathematical pseudocode: its 2^32 denotes exponentiation, rather than C's XOR operator.
The displayed A32 routine assembled successfully with Clang and was disassembled to check the conditional and non-flag-setting forms. Its carry flag model was checked exhaustively at an eight-bit machine word size and against boundary and randomized 32-bit divisions. No native ARM architecture family execution is claimed.
Use a logical right shift to discard one inspected bit at a time. The following Thumb instruction set subroutine accepts the word in r0, returns its population count in r0, preserves r4, and otherwise uses caller-saved processor registers.
popcount_loop:
    push {r4}
    movs r1, #0
    movs r2, #1
    movs r3, #32
pop_loop:
    movs r4, r0
    ands r4, r4, r2
    adds r1, r1, r4
    lsrs r0, r0, #1
    subs r3, r3, #1
    bne pop_loop
    movs r0, r1
    pop {r4}
    bx lr
ands extracts the current least significant bit; adds includes it in the count. lsrs fills the newly vacant high position with zero. subs immediately precedes bne, so the branch tests the zero flag from decrementing the loop counter, not a flag left by the shift.
After iterations, the accumulator contains the number of one bits in the original word's lowest positions, and the remaining word is the original unsigned interpretation shifted right by . This loop invariant holds initially and is preserved by each iteration. At every position has been inspected, proving the result. The given example therefore returns 10, and the all-zero and all-one words return 0 and 32. The accumulator cannot overflow: it never exceeds 32.
Use the AAPCS32 convention: r0 holds the input address, r1 the length, and r0 receives the result. The following Thumb instruction set assembly language uses only scratch processor registers, so no callee-saved registers need saving. Assume the array is valid, halfword-aligned, and has nonnegative length; for the mathematical sum assume it fits a signed 32-bit two's complement integer.
.syntax unified
.thumb
.thumb_func
sum16:
    movs r2, #0             @ accumulated sum
    cmp  r1, #0
    ble  .Ldone             @ empty array; also returns 0 if n < 0
.Lloop:
    movs r3, #0             @ Thumb signed load needs a register offset
    ldrsh r3, [r0, r3]      @ signed halfword at a; offset read before r3 changes
    adds r2, r2, r3         @ include this signed value
    adds r0, r0, #2         @ advance by one short, not one byte
    subs r1, r1, #1         @ one fewer element remains
    bne  .Lloop
.Ldone:
    movs r0, r2             @ result register
    bx   lr                 @ leaf routine: lr was not overwritten
After iterations, the loop invariant is that r2 contains the first elements' sum, r0 points to element , and r1 is . The sign extension in ldrsh supplies the correct signed addend. One iteration preserves this loop invariant, and the decreasing remaining length proves algorithm termination. For the seven-element calls, the sum lies between and , so it fits. For longer arrays that overflow, these instructions compute the sum modulo ; that is not a claim about defined signed C programming language overflow behavior. The operand forms are those of the paper's reference sheet.[ref]
Solutions to all eight questions of the 7 September 2022 resit. Released Oxford examination archive, member 2021/L/a10101w1.pdf. The dated cover establishes the year despite the undated held filename and misleading archive directory. The Thumb instruction set listings assume the 32-bit AAPCS32 data layout; the timer calculation in Question 2 corrects a numerical slip in the paper.
Use an explicit-stack depth-first tree traversal. Descend the left branch immediately and put each nonempty right subtree on a stack for later. For the same node declarations as in the paper, an original C programming language implementation is:
int sum_iter(tree t)
{
    tree pending[32];
    unsigned top = 0;
    int total = 0;
    while (t != NULL || top != 0) {
        if (t == NULL) {
            t = pending[--top];
        } else {
            total += t->data;
            if (t->right != NULL)
                pending[top++] = t->right;
            t = t->left;
        }
    }
    return total;
}
The loop invariant says that the accumulated labels plus the sums of the current subtree and all pending subtrees equal the original tree sum, with the unvisited subtrees disjoint. Visiting a node adds its label and replaces its subtree by its two children. Popping merely changes which pending subtree is current. When both current and pending subtrees are absent, the invariant proves the result.
At most one unvisited right sibling is pending at each level of the current ancestor path. If depth means the number of nonempty nodes on a longest root-to-leaf path, depth at most 32 requires at most 31 pending right siblings. A 32-word array is therefore sufficient; it also covers the convention that depth counts at most 32 edges. The depth promise makes unchecked pushes valid here. For arbitrary input, a production implementation should check capacity rather than overwrite its frame.
A corresponding Thumb instruction set implementation keeps the total in r4, the array base in r5, and its next-free address in r6:
.global sum_iter_asm
.thumb_func
sum_iter_asm:
    push {r4, r5, r6, lr}
    sub  sp, sp, #128        @ 32 pending pointers
    mov  r5, sp              @ base of pending array
    mov  r6, sp              @ empty stack: next-free equals base
    movs r4, #0
.Lvisit:
    cmp  r0, #0
    beq  .Ltake_pending
    ldr  r1, [r0, #0]
    adds r4, r4, r1          @ consume this node's label
    ldr  r1, [r0, #8]
    cmp  r1, #0
    beq  .Lleft
    str  r1, [r6, #0]        @ defer the nonempty right subtree
    adds r6, r6, #4
.Lleft:
    ldr  r0, [r0, #4]        @ continue down the left branch
    b    .Lvisit
.Ltake_pending:
    cmp  r6, r5
    beq  .Ldone
    subs r6, r6, #4
    ldr  r0, [r6, #0]
    b    .Lvisit
.Ldone:
    movs r0, r4
    add  sp, sp, #128
    pop  {r4, r5, r6, pc}
The stack pointer stays fixed while the array is used; r6 is a separate cursor inside that array. All callee-saved registers touched by the function are restored. Each nonempty node contributes its label exactly once. As in part (a), require that every partial total fits the signed result type; a representable final answer alone does not prevent overflow in a different summation order.