import type { AvmContext } from '../avm_context.js'; import { getBitwiseDynamicGasMultiplier } from '../avm_gas.js'; import { type IntegralValue, TaggedMemory, type TaggedMemoryInterface, TypeTag } from '../avm_memory_types.js'; import { Opcode, OperandType } from '../serialization/instruction_serialization.js'; import { Addressing } from './addressing_mode.js'; import { Instruction } from './instruction.js'; import { ThreeOperandInstruction } from './instruction_impl.js'; abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction { public async execute(context: AvmContext): Promise { const memory = context.machineState.memory; const addressing = Addressing.fromWire(this.addressingMode); context.machineState.consumeGas( this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()), ); const operands = [this.aOffset, this.bOffset, this.dstOffset]; const [aOffset, bOffset, dstOffset] = addressing.resolve(operands, memory); this.checkTags(memory, aOffset, bOffset); const multiplier = this.getDynamicMultiplier(memory.getTag(aOffset)); context.machineState.consumeGas(this.dynamicGasCost(multiplier)); const a = memory.getAs(aOffset); const b = memory.getAs(bOffset); const res = this.compute(a, b); memory.set(dstOffset, res); } protected abstract compute(a: IntegralValue, b: IntegralValue): IntegralValue; protected checkTags(memory: TaggedMemoryInterface, aOffset: number, bOffset: number) { TaggedMemory.checkIsIntegralTag(memory.getTag(aOffset)); memory.checkTagsAreSame(aOffset, bOffset); } protected getDynamicMultiplier(_lhsTag: TypeTag): number { return 0; } } export class And extends ThreeOperandBitwiseInstruction { static readonly type: string = 'AND'; static readonly opcode = Opcode.AND_8; // FIXME: needed for gas. protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue { return a.and(b); } protected override getDynamicMultiplier(lhsTag: TypeTag): number { return getBitwiseDynamicGasMultiplier(lhsTag); } } export class Or extends ThreeOperandBitwiseInstruction { static readonly type: string = 'OR'; static readonly opcode = Opcode.OR_8; // FIXME: needed for gas. protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue { return a.or(b); } protected override getDynamicMultiplier(lhsTag: TypeTag): number { return getBitwiseDynamicGasMultiplier(lhsTag); } } export class Xor extends ThreeOperandBitwiseInstruction { static readonly type: string = 'XOR'; static readonly opcode = Opcode.XOR_8; // FIXME: needed for gas. protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue { return a.xor(b); } protected override getDynamicMultiplier(lhsTag: TypeTag): number { return getBitwiseDynamicGasMultiplier(lhsTag); } } export class Not extends Instruction { static readonly type: string = 'NOT'; static readonly opcode = Opcode.NOT_8; static readonly wireFormat8 = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT8, OperandType.UINT8]; static readonly wireFormat16 = [OperandType.UINT8, OperandType.UINT8, OperandType.UINT16, OperandType.UINT16]; constructor( private addressingMode: number, private srcOffset: number, private dstOffset: number, ) { super(); } public async execute(context: AvmContext): Promise { const memory = context.machineState.memory; const addressing = Addressing.fromWire(this.addressingMode); context.machineState.consumeGas( this.baseGasCost(addressing.indirectOperandsCount(), addressing.relativeOperandsCount()), ); const operands = [this.srcOffset, this.dstOffset]; const [srcOffset, dstOffset] = addressing.resolve(operands, memory); TaggedMemory.checkIsIntegralTag(memory.getTag(srcOffset)); const value = memory.getAs(srcOffset); const res = value.not(); memory.set(dstOffset, res); } }