Skip to main content
This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.

Overview

Opcode: 0x58 Introduced: Frontier (EVM genesis) PC pushes the current program counter value onto the stack. The program counter represents the position of the currently executing instruction in the bytecode. This opcode enables position-aware bytecode, dynamic jump calculations, and self-referential code patterns.

Specification

Stack Input: None Stack Output:
Gas Cost: 2 (GasQuickStep) Operation:

Behavior

PC provides the current execution position:
  1. Consumes 2 gas (GasQuickStep)
  2. Pushes current PC value onto stack
  3. Increments PC to next instruction
Important: The value pushed is the PC of the PC instruction itself, NOT the next instruction. Example:
When PC executes at position 0x02, it pushes 0x02 onto the stack.

Examples

Basic PC Usage

Calculate Relative Position

Position-Aware Code

Dynamic Jump Table

Gas Cost

Cost: 2 gas (GasQuickStep) Comparison:
  • PC: 2 gas (read position)
  • PUSH1: 3 gas (push constant)
  • JUMP: 8 gas
  • JUMPI: 10 gas
Efficiency: PC is cheaper than PUSH for getting current position, but PUSH is still preferred for constants.

Edge Cases

PC at Start

PC at End

Stack Overflow

Multiple PC Calls

Common Usage

Relative Addressing

Calculate addresses relative to current position:

Position Verification

Code Size Calculation

Dynamic Dispatch Base

Implementation

Testing

Test Coverage

Security

Position-Dependent Code

PC enables position-dependent bytecode, which can be fragile:
Issues:
  • Compiler optimizations can change positions
  • Adding code shifts all offsets
  • Hard to maintain and debug
Better approach:

Code Obfuscation

PC can be used for code obfuscation:
Security impact:
  • Makes auditing difficult
  • Hides control flow
  • Red flag for malicious code
  • Avoid in production contracts

Limited Practical Use

PC has few legitimate use cases in modern Solidity: Not useful for:
  • Function dispatch (compiler handles this)
  • Relative jumps (labels are better)
  • Code size (codesize opcode exists)
Occasionally useful for:
  • Gas optimization (avoid PUSH for position)
  • Self-modifying code patterns (advanced, rare)
  • Position verification in tests

Compiler Behavior

Solidity Avoids PC

Modern Solidity rarely generates PC instructions:
Compiles to:
But normal Solidity doesn’t use PC - it uses PUSH for constants and labels for jumps.

Labels vs PC

Old pattern (PC-based):
Modern pattern (label-based):
Compiler resolves labels to absolute positions at compile time.

Optimization

Compilers can optimize PC away:
Could be optimized to:

Comparison with Other Chains

EVM (Ethereum)

PC returns bytecode position, 0-indexed.

WASM

No direct equivalent. WASM uses structured control flow (blocks, loops) rather than position-based jumps.

x86 Assembly

EIP (Extended Instruction Pointer) register similar to PC, but:
  • Hardware register, not stack
  • 64-bit on x64, 32-bit on x86
  • Can be read/written directly

JVM

No equivalent. JVM uses structured bytecode with exception tables rather than position-based control flow.

Historical Context

PC was included in original EVM for:
  1. Position-aware code patterns
  2. Relative addressing calculations
  3. Dynamic jump table construction
Modern usage:
  • Rarely needed in practice
  • Compilers use labels instead
  • Maintained for compatibility
  • Occasionally useful for gas optimization

References