Skip to content

Commit

Permalink
feat(avm): implement addressing modes for MOV (#4490)
Browse files Browse the repository at this point in the history
Creates `Addressing` class to handle addressing modes.
I might later change the instruction constructors to take/accept `Addressing`.

Closes: #4266.
  • Loading branch information
fcarreiro authored Feb 8, 2024
1 parent bc25f9b commit ab4eaf0
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 54 deletions.
49 changes: 49 additions & 0 deletions yarn-project/simulator/src/avm/opcodes/addressing_mode.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { TaggedMemory, Uint32 } from '../avm_memory_types.js';
import { Addressing, AddressingMode } from './addressing_mode.js';

describe('Addressing', () => {
it('should reserialize correctly', () => {
const addressingMode = Addressing.fromWire(0b10101010);
const wireModes = addressingMode.toWire();
expect(wireModes).toBe(0b10101010);
});

it('should convert to wire format correctly', () => {
const addressingMode = new Addressing([
AddressingMode.INDIRECT,
AddressingMode.DIRECT,
AddressingMode.INDIRECT,
AddressingMode.DIRECT,
]);
const wireModes = addressingMode.toWire();
expect(wireModes).toBe(0b00000101);
});

it('should convert from wire format correctly', () => {
const addressingMode = Addressing.fromWire(0b00011001);
const expected = new Addressing([
AddressingMode.INDIRECT,
AddressingMode.DIRECT,
AddressingMode.DIRECT,
AddressingMode.INDIRECT,
AddressingMode.INDIRECT,
AddressingMode.DIRECT,
AddressingMode.DIRECT,
AddressingMode.DIRECT,
]);

expect(addressingMode).toStrictEqual(expected);
});

it('should resolve offsets correctly', () => {
const addressingMode = Addressing.fromWire(0b00011001);
const offsets = [10, 20, 30];
const mem = new TaggedMemory();
mem.set(10, new Uint32(100));
mem.set(20, new Uint32(200));
mem.set(30, new Uint32(300));

const resolved = addressingMode.resolve(offsets, mem);
expect(resolved).toEqual([100, 20, 30]);
});
});
66 changes: 66 additions & 0 deletions yarn-project/simulator/src/avm/opcodes/addressing_mode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { strict as assert } from 'assert';

import { TaggedMemory, TypeTag } from '../avm_memory_types.js';

export enum AddressingMode {
DIRECT,
INDIRECT,
INDIRECT_PLUS_CONSTANT, // Not implemented yet.
}

/** A class to represent the addressing mode of an instruction. */
export class Addressing {
public constructor(
/** The addressing mode for each operand. The length of this array is the number of operands of the instruction. */
private readonly modePerOperand: AddressingMode[],
) {
assert(modePerOperand.length <= 8, 'At most 8 operands are supported');
}

public static fromWire(wireModes: number): Addressing {
// The modes are stored in the wire format as a byte, with each bit representing the mode for an operand.
// The least significant bit represents the zeroth operand, and the most significant bit represents the last operand.
const modes = new Array<AddressingMode>(8);
for (let i = 0; i < 8; i++) {
modes[i] = (wireModes & (1 << i)) === 0 ? AddressingMode.DIRECT : AddressingMode.INDIRECT;
}
return new Addressing(modes);
}

public toWire(): number {
// The modes are stored in the wire format as a byte, with each bit representing the mode for an operand.
// The least significant bit represents the zeroth operand, and the least significant bit represents the last operand.
let wire: number = 0;
for (let i = 0; i < 8; i++) {
if (this.modePerOperand[i] === AddressingMode.INDIRECT) {
wire |= 1 << i;
}
}
return wire;
}

/**
* Resolves the offsets using the addressing mode.
* @param offsets The offsets to resolve.
* @param mem The memory to use for resolution.
* @returns The resolved offsets. The length of the returned array is the same as the length of the input array.
*/
public resolve(offsets: number[], mem: TaggedMemory): number[] {
assert(offsets.length <= this.modePerOperand.length);
const resolved = new Array(offsets.length);
for (const [i, offset] of offsets.entries()) {
switch (this.modePerOperand[i]) {
case AddressingMode.INDIRECT:
mem.checkTag(TypeTag.UINT32, offset);
resolved[i] = Number(mem.get(offset).toBigInt());
break;
case AddressingMode.DIRECT:
resolved[i] = offset;
break;
default:
throw new Error(`Unimplemented addressing mode: ${AddressingMode[this.modePerOperand[i]]}`);
}
}
return resolved;
}
}
11 changes: 11 additions & 0 deletions yarn-project/simulator/src/avm/opcodes/memory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AvmContext } from '../avm_context.js';
import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js';
import { InstructionExecutionError } from '../errors.js';
import { initContext, initExecutionEnvironment } from '../fixtures/index.js';
import { Addressing, AddressingMode } from './addressing_mode.js';
import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js';

describe('Memory instructions', () => {
Expand Down Expand Up @@ -304,6 +305,16 @@ describe('Memory instructions', () => {
expect(tag).toEqual(TypeTag.UINT16);
});

it('Should support INDIRECT addressing', async () => {
context.machineState.memory.set(0, new Uint16(55));
context.machineState.memory.set(10, new Uint32(20));
const addressing = new Addressing([/*srcOffset*/ AddressingMode.DIRECT, /*dstOffset*/ AddressingMode.INDIRECT]);
await new Mov(/*indirect=*/ addressing.toWire(), /*srcOffset=*/ 0, /*dstOffset=*/ 10).execute(context);

expect(context.machineState.memory.get(1)).toBeUndefined();
expect(context.machineState.memory.get(20)).toEqual(new Uint16(55n));
});

it('Should move field elements on different memory cells', async () => {
context.machineState.memory.set(0, new Field(27));
await new Mov(/*indirect=*/ 0, /*srcOffset=*/ 0, /*dstOffset=*/ 1).execute(context);
Expand Down
10 changes: 8 additions & 2 deletions yarn-project/simulator/src/avm/opcodes/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Field, TaggedMemory, TypeTag } from '../avm_memory_types.js';
import { InstructionExecutionError } from '../errors.js';
import { BufferCursor } from '../serialization/buffer_cursor.js';
import { Opcode, OperandType, deserialize, serialize } from '../serialization/instruction_serialization.js';
import { Addressing } from './addressing_mode.js';
import { Instruction } from './instruction.js';
import { TwoOperandInstruction } from './instruction_impl.js';

Expand Down Expand Up @@ -152,9 +153,14 @@ export class Mov extends Instruction {
}

async execute(context: AvmContext): Promise<void> {
const a = context.machineState.memory.get(this.srcOffset);
const [srcOffset, dstOffset] = Addressing.fromWire(this.indirect).resolve(
[this.srcOffset, this.dstOffset],
context.machineState.memory,
);

context.machineState.memory.set(this.dstOffset, a);
const a = context.machineState.memory.get(srcOffset);

context.machineState.memory.set(dstOffset, a);

context.machineState.incrementPc();
}
Expand Down
Loading

0 comments on commit ab4eaf0

Please sign in to comment.