INTRODUCTION
enum class OrderType { BUY, SELL };
enum class OrderType2 : char { BUY='B', SELL='S' };
Two enum types.
First: default underlying type (int).
Second: explicit underlying type (char).
Measurements:
- sizeof operator
- Assembly instructions (movl vs movb)
- Machine code bytes
- Function calls (operator<<)
- Benchmark with cout (10M iterations)
- Benchmark without cout (100M iterations)
- Ternary operator assembly (cmovnel vs jne)
Compiler: clang++ 18.1.3
Flags: -std=c++23 -O0
Target: x86_64-pc-linux-gnu
All data from actual compilation and execution.
All assembly from .s files.
All timings from benchmark runs.

LESSON 1: STORAGE SIZE
enum class OrderType { BUY, SELL };

OrderType type1 = OrderType::SELL;
sizeof(OrderType): 4

LESSON 2: CHAR STORAGE SIZE
enum class OrderType2 : char { BUY='B', SELL='S' };

OrderType2 type2 = OrderType2::SELL;
sizeof(OrderType2): 1

LESSON 3: ASCII VALUES
static_cast<int>('B') = 66
static_cast<int>('S') = 83
66 = 0x42
83 = 0x53

LESSON 4: ASSEMBLY INSTRUCTION INT
AXIOM: Register
Register = storage location inside CPU.
%rbp = base pointer register (64-bit).
AXIOM: Stack
Stack = memory region for local variables.
Stack grows downward (higher addresses to lower addresses).
AXIOM: Stack Offset
-8(%rbp) = memory address 8 bytes below base pointer.
rbp = 0x7fff1000 (example)
rbp - 8 = 0x7fff0ff8
AXIOM: Immediate Value
$1 = literal value 1 (not a memory address).
AXIOM: movl Instruction
movl = move long (4 bytes).
movl SOURCE, DESTINATION
Copies 4 bytes from SOURCE to DESTINATION.
OrderType type = OrderType::SELL;
OrderType::SELL = 1
clang++ -std=c++23 -S -O0 enum_storage.cpp
movl    $1, -8(%rbp)
$1 = immediate value 1
-8(%rbp) = stack location 8 bytes below rbp
movl = copy 4 bytes
Action: Store value 1 (4 bytes) at memory address rbp-8

LESSON 5: ASSEMBLY INSTRUCTION CHAR
AXIOM: movb Instruction
movb = move byte (1 byte).
movb SOURCE, DESTINATION
Copies 1 byte from SOURCE to DESTINATION.
OrderType2 type2 = OrderType2::SELL;
OrderType2::SELL = 'S' = 83
clang++ -std=c++23 -S -O0 enum_storage.cpp
movb    $83, -9(%rbp)
$83 = immediate value 83
-9(%rbp) = stack location 9 bytes below rbp
movb = copy 1 byte
Action: Store value 83 (1 byte) at memory address rbp-9

LESSON 6: MACHINE CODE INT
AXIOM: Machine Code
Machine code = binary instructions CPU executes.
Displayed in hexadecimal.
AXIOM: Opcode
Opcode = operation code (first byte(s) of instruction).
Tells CPU what operation to perform.
AXIOM: Operands
Operands = data following opcode.
Specify addresses, registers, immediate values.
objdump -d test_enum
115f:   c7 45 f8 01 00 00 00    movl   $0x1,-0x8(%rbp)
115f = memory address of instruction
c7 45 f8 01 00 00 00 = machine code bytes
movl $0x1,-0x8(%rbp) = assembly representation
c7 45 f8 01 00 00 00
byte 0: c7                        # movl opcode
byte 1: 45                        # ModR/M byte (addressing mode)
byte 2: f8                        # displacement -8 (0xf8 = -8 in two's complement)
byte 3: 01                        # immediate value byte 0
byte 4: 00                        # immediate value byte 1
byte 5: 00                        # immediate value byte 2
byte 6: 00                        # immediate value byte 3
0x01 00 00 00 = 1 in little-endian (least significant byte first)
7 bytes total

LESSON 7: MACHINE CODE CHAR
objdump -d test_enum
1166:   c6 45 f7 53             movb   $0x53,-0x9(%rbp)
1166 = memory address of instruction
c6 45 f7 53 = machine code bytes
movb $0x53,-0x9(%rbp) = assembly representation
c6 45 f7 53
byte 0: c6                        # movb opcode
byte 1: 45                        # ModR/M byte (addressing mode)
byte 2: f7                        # displacement -9 (0xf7 = -9 in two's complement)
byte 3: 53                        # immediate value 83 (0x53 = 83)
4 bytes total
7 bytes (movl) - 4 bytes (movb) = 3 bytes difference

LESSON 8: PRINT INT ENUM
AXIOM: Function Call
Function call = transfer control to another code location.
callq = call instruction (64-bit).
AXIOM: x86-64 Calling Convention
Parameter 1: %rdi register
Parameter 2: %rsi register
Parameter 3: %rdx register
(and so on)
AXIOM: this Pointer
Member function receives object address as first parameter.
std::cout is an object.
operator<< is a member function.
AXIOM: Name Mangling
C++ encodes function signatures in symbol names.
_ZNSolsEi = mangled name
c++filt = tool to demangle names
OrderType type = OrderType::BUY;
std::cout << static_cast<int>(type);
OrderType::BUY = 0
clang++ -std=c++23 -S -O0 test_enum.cpp
movl    $0, -8(%rbp)                          # store enum value 0 on stack
movl    -8(%rbp), %esi                        # load value into esi (2nd parameter)
movq    _ZSt4cout@GOTPCREL(%rip), %rdi        # load cout address into rdi (1st parameter)
callq   _ZNSolsEi@PLT                         # call function
Step 1: Store 0 at stack location rbp-8
Step 2: Load value from rbp-8 into %esi (32-bit portion of %rsi)
Step 3: Load cout object address into %rdi
Step 4: Call function at _ZNSolsEi
c++filt _ZNSolsEi
std::ostream::operator<<(int)
Function signature: ostream& operator<<(ostream* this, int value)
Parameter 1 (%rdi): this pointer (cout address)
Parameter 2 (%esi): int value (0)

LESSON 9: PRINT CHAR ENUM
AXIOM: Sign Extension
movsbl = move with sign extension byte to long.
Copies 1 byte, extends to 4 bytes, preserving sign.
83 = 0x53 = 0b01010011
Sign bit (leftmost) = 0 (positive)
Extended to 32 bits: 0x00000053
OrderType2 type2 = OrderType2::SELL;
std::cout << static_cast<char>(type2);
OrderType2::SELL = 'S' = 83
clang++ -std=c++23 -S -O0 test_enum.cpp
movb    $83, -9(%rbp)                         # store enum value 83 on stack
movsbl  -9(%rbp), %esi                        # load byte with sign extension into esi
movq    _ZSt4cout@GOTPCREL(%rip), %rdi        # load cout address into rdi
callq   _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c@PLT
Step 1: Store 83 at stack location rbp-9 (1 byte)
Step 2: Load 1 byte from rbp-9, extend to 4 bytes, store in %esi
Step 3: Load cout object address into %rdi
Step 4: Call function
c++filt _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c
std::basic_ostream<char, std::char_traits<char> >& 
std::operator<< <std::char_traits<char> >
(std::basic_ostream<char, std::char_traits<char> >&, char)
Function signature: ostream& operator<<(ostream& os, char c)
Parameter 1 (%rdi): ostream reference (cout address)
Parameter 2 (%esi): char value (83, extended to 32-bit)

LESSON 10: BENCHMARK INT WITH COUT
for (int i = 0; i < 10000000; ++i) {
    OrderType type = (i & 1) ? OrderType::SELL : OrderType::BUY;
    null_stream << static_cast<int>(type);
}
./enum_benchmark
Run 1: int enum (cast to int): 342 ms
Run 2: int enum (cast to int): 328 ms
Run 3: int enum (cast to int): 335 ms
(342 + 328 + 335) / 3 = 335
335 ms / 10000000 = 0.0000335 ms = 33.5 nanoseconds

LESSON 11: BENCHMARK CHAR WITH COUT
for (int i = 0; i < 10000000; ++i) {
    OrderType2 type = (i & 1) ? OrderType2::SELL : OrderType2::BUY;
    null_stream << static_cast<char>(type);
}
./enum_benchmark
Run 1: char enum (direct cast): 97 ms
Run 2: char enum (direct cast): 93 ms
Run 3: char enum (direct cast): 95 ms
(97 + 93 + 95) / 3 = 95
95 ms / 10000000 = 0.0000095 ms = 9.5 nanoseconds

LESSON 12: RATIO WITH COUT
335 ms (int)
95 ms (char)
335 / 95 = 3.53

LESSON 13: BENCHMARK INT WITHOUT COUT
for (int i = 0; i < 100000000; ++i) {
    OrderType type = (i & 1) ? OrderType::SELL : OrderType::BUY;
    sink_int = static_cast<int>(type);
}
./pure_enum_test
Run 1: int enum: 255 ms
Run 2: int enum: 254 ms
Run 3: int enum: 253 ms
Run 4: int enum: 252 ms
(255 + 254 + 253 + 252) / 4 = 254
254 ms / 100000000 = 0.00000254 ms = 2.54 nanoseconds

LESSON 14: BENCHMARK CHAR WITHOUT COUT
for (int i = 0; i < 100000000; ++i) {
    OrderType2 type = (i & 1) ? OrderType2::SELL : OrderType2::BUY;
    sink_char = static_cast<char>(type);
}
./pure_enum_test
Run 1: char enum: 298 ms
Run 2: char enum: 298 ms
Run 3: char enum: 294 ms
Run 4: char enum: 295 ms
(298 + 298 + 294 + 295) / 4 = 296
296 ms / 100000000 = 0.00000296 ms = 2.96 nanoseconds

LESSON 15: RATIO WITHOUT COUT
254 ms (int)
296 ms (char)
296 / 254 = 1.17

LESSON 16: TERNARY CONDITION
AXIOM: Bitwise AND
& = bitwise AND operator
Compares each bit position
1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
i = 5
i & 1 = 5 & 1
5 = 0b0101
1 = 0b0001
  0b0101
& 0b0001
  ------
  0b0001 = 1
AXIOM: Ternary Operator
condition ? value_if_true : value_if_false
If condition != 0, result = value_if_true
If condition == 0, result = value_if_false
1 != 0
Result: OrderType::SELL

LESSON 17: TERNARY INT ASSEMBLY
AXIOM: CPU Flags
CPU has flags register with status bits.
ZF = Zero Flag
ZF = 1 if result is zero
ZF = 0 if result is non-zero
AXIOM: cmpl Instruction
cmpl = compare long (4 bytes)
cmpl SOURCE, DESTINATION
Performs: DESTINATION - SOURCE
Sets flags based on result
Does not store result
AXIOM: cmovnel Instruction
cmovnel = conditional move if not equal (long)
cmovnel SOURCE, DESTINATION
If ZF = 0: DESTINATION = SOURCE
If ZF = 1: DESTINATION unchanged
AXIOM: xorl Instruction
xorl = XOR long (4 bytes)
xorl %eax, %eax = %eax XOR %eax
Any value XOR itself = 0
Fast way to set register to 0
type = (i & 1) ? OrderType::SELL : OrderType::BUY;
i = 5
OrderType::BUY = 0
OrderType::SELL = 1
clang++ -std=c++23 -S -O0 pure_enum_test.cpp
movl    -12(%rbp), %edx           # load i from stack into edx
andl    $1, %edx                  # edx = edx & 1
xorl    %eax, %eax                # eax = 0
movl    $1, %ecx                  # ecx = 1
cmpl    $0, %edx                  # compare edx with 0, set flags
cmovnel %ecx, %eax                # if ZF=0, eax = ecx
movl    %eax, -16(%rbp)           # store result to stack
movl    -16(%rbp), %eax           # load result back
movl    %eax, sink_int(%rip)      # store to volatile variable
Step 1: edx = Memory[rbp-12] = 5
Step 2: edx = 5 & 1 = 1
Step 3: eax = eax XOR eax = 0
Step 4: ecx = 1
Step 5: Compare: 1 - 0 = 1 (non-zero), ZF = 0
Step 6: ZF = 0 (not equal), eax = ecx = 1
Step 7: Memory[rbp-16] = 1
Step 8: eax = Memory[rbp-16] = 1
Step 9: Memory[sink_int] = 1
Instruction count:
1. movl
2. andl
3. xorl
4. movl
5. cmpl
6. cmovnel
7. movl
8. movl
9. movl
9 instructions
cmovnel = conditional move (no jump)
Branch count: 0

LESSON 18: TERNARY CHAR ASSEMBLY
AXIOM: jne Instruction
jne = jump if not equal
jne LABEL
If ZF = 0: PC = address of LABEL
If ZF = 1: PC = next instruction
PC = Program Counter (instruction pointer)
AXIOM: Branch
Branch = instruction that changes PC based on condition.
Creates two possible execution paths.
CPU must predict which path to take.
AXIOM: Register Spilling
Spilling = storing register value to stack.
Happens when compiler runs out of registers.
AXIOM: x86-64 8-bit Registers
%al = low 8 bits of %rax
%dl = low 8 bits of %rdx
%cl = low 8 bits of %rcx
Limited number of 8-bit registers available
AXIOM: No cmovneb
x86-64 has cmovnel (32-bit conditional move)
x86-64 has cmovneq (64-bit conditional move)
x86-64 does NOT have cmovneb (8-bit conditional move)
Compiler must use branch for 8-bit values
type = (i & 1) ? OrderType2::SELL : OrderType2::BUY;
i = 5
OrderType2::BUY = 'B' = 66
OrderType2::SELL = 'S' = 83
clang++ -std=c++23 -S -O0 pure_enum_test.cpp
movl    -12(%rbp), %ecx           # load i from stack into ecx
andl    $1, %ecx                  # ecx = ecx & 1
movb    $83, %al                  # al = 83 (SELL)
movb    $66, %dl                  # dl = 66 (BUY)
movb    %dl, -42(%rbp)            # spill BUY to stack
cmpl    $0, %ecx                  # compare ecx with 0, set flags
movb    %al, -41(%rbp)            # spill SELL to stack
jne     .LBB4_6                   # if ZF=0, jump to .LBB4_6
movb    -42(%rbp), %al            # reload BUY from stack
movb    %al, -41(%rbp)            # store BUY to result location
.LBB4_6:                          # jump target label
movb    -41(%rbp), %al            # load result from stack
movb    %al, -13(%rbp)            # store to local variable
movb    -13(%rbp), %al            # load local variable back
movb    %al, sink_char(%rip)      # store to volatile variable
Step 1: ecx = Memory[rbp-12] = 5
Step 2: ecx = 5 & 1 = 1
Step 3: al = 83
Step 4: dl = 66
Step 5: Memory[rbp-42] = 66 (spill BUY)
Step 6: Compare: 1 - 0 = 1 (non-zero), ZF = 0
Step 7: Memory[rbp-41] = 83 (spill SELL)
Step 8: ZF = 0 (not equal), PC jumps to .LBB4_6
Step 9: (skipped: instructions between jne and .LBB4_6)
Step 10: al = Memory[rbp-41] = 83
Step 11: Memory[rbp-13] = 83
Step 12: al = Memory[rbp-13] = 83
Step 13: Memory[sink_char] = 83
Instruction count:
1. movl
2. andl
3. movb
4. movb
5. movb
6. cmpl
7. movb
8. jne
9. movb (skipped in this execution)
10. movb (skipped in this execution)
11. movb
12. movb
13. movb
14. movb
14 instructions total (12 executed in this path)
jne = jump (branch instruction)
Branch count: 1
Stack operations:
Memory[rbp-42] = 66 (spill)
Memory[rbp-41] = 83 (spill)
Memory[rbp-13] = 83 (local variable)
Memory[sink_char] = 83 (volatile)
4 stack write operations
Comparison:
int enum: 9 instructions, 0 branches, cmovnel
char enum: 14 instructions, 1 branch, jne

SOURCE CODE
enum_storage.cpp         sizeof measurements
enum_storage.s           assembly for sizeof test
test_enum.cpp            cout operator<< test
test_enum.s              assembly for cout test
enum_benchmark.cpp       10M iterations with cout
pure_enum_test.cpp       100M iterations without cout
pure_enum_test.s         assembly for ternary operator

WORKSHEETS
01-axioms-memory.md              sizeof measurements and ASCII values
02-assembly-instructions.md      movl vs movb analysis
03-enum-storage-proof.md         storage verification
05-operator-int-assembly.md      operator<<(int) disassembly
06-operator-char-assembly.md     operator<<(char) disassembly
07-benchmark-methodology.md      benchmark setup
08-benchmark-results-proof.md    timing measurements
BRANCH_VS_CMOV_AXIOMS.md         cmovnel vs jne analysis

REPRODUCTION
clang++ -std=c++23 enum_storage.cpp -o enum_storage
./enum_storage

clang++ -std=c++23 -S -O0 test_enum.cpp -o test_enum.s
clang++ -std=c++23 -O0 test_enum.cpp -o test_enum
objdump -d test_enum

clang++ -std=c++23 -O0 enum_benchmark.cpp -o enum_benchmark
./enum_benchmark

clang++ -std=c++23 -S -O0 pure_enum_test.cpp -o pure_enum_test.s
clang++ -std=c++23 -O0 pure_enum_test.cpp -o pure_enum_test
./pure_enum_test

COMPILER
Ubuntu clang version 18.1.3 (1ubuntu1)
Target: x86_64-pc-linux-gnu