Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SASS Instruction Encoding

All addresses in this page apply to ptxas v13.0.88 (CUDA 13.0). Other versions will differ.

The SASS instruction encoder is the single largest subsystem in ptxas by function count. It translates the internal Ori IR instruction representation into packed binary SASS machine code for a specific SM target. The encoder comprises approximately 4,000 template-generated handler functions dispatched through function-pointer tables indexed by opcode, plus six massive switch-dispatch megafunctions that route field-level queries by instruction category. The core encoding primitive is a single 216-byte bitfield-insert function (sub_7B9B80) called from 18,347 sites throughout the binary. NVIDIA internally names this pipeline phase "Ori Phase Encoding" within the Mercury assembler backend.

Pipeline phaseOriPhaseEncoding (within Mercury)
Core bitfield packersub_7B9B80 (216 bytes, 18,347 callers)
Encoding buffer1280 bits = 20 QWORDs at a1+544
Instruction widths64-bit (format 1), 128-bit (format 2), 256-bit (format 8)
Opcode hierarchy3-level: major (9 bits) / minor (8 bits) / sub-opcode (7 bits)
SM100 encoder count~1,086 encode functions + ~97 decode functions
SM100 opcode categories370 (case values 0x0 through 0x171)
SM100 major opcodes102 unique values
Bitfield accessor primitives2,095 functions (mostly under 200 bytes)
Confirmed strings"AdvancedPhaseOriPhaseEncoding", "MercEncodeAndDecode", "After EncodeAndDecode", "ENCODING"

Encoding Buffer Layout

Every encoder operates on an instruction encoding context object passed as a1. The primary encoding target is a 1280-bit (160-byte, 20 QWORD) buffer at offset a1+544. The bitfield packer sub_7B9B80 writes individual fields into this buffer by iterating in 64-bit chunks:

// sub_7B9B80(a1, bit_offset, bit_width, value)
// Insert `value` into bits [bit_offset .. bit_offset+bit_width) of the encoding buffer
void bitfield_insert(int64_t a1, int bit_offset, int bit_width, uint64_t value) {
    uint64_t mask = (1ULL << bit_width) - 1;
    value &= mask;
    int pos = bit_offset;
    while (pos < 1280) {
        int qword_idx = pos >> 6;           // which QWORD
        int bit_in_qword = pos & 63;        // bit position within QWORD
        *(uint64_t*)(a1 + 8 * qword_idx + 544) |= value << bit_in_qword;
        // Handle fields that cross a QWORD boundary
        if (bit_in_qword + bit_width > 64) {
            int overflow = bit_in_qword + bit_width - 64;
            *(uint64_t*)(a1 + 8 * (qword_idx + 1) + 544) |= value >> (64 - bit_in_qword);
        }
        break;  // single insertion (loop structure handles interleaved format)
    }
}

The encoding context object has this layout:

OffsetSizeContent
+0x0008Bvtable / allocator pointer
+0x00816BFormat descriptor (xmmword constant from rodata)
+0x0104BBitfield position base index
+0x018120BRegister class maps (3 arrays of 10 DWORDs: source classes, dest classes, widths)
+0x0904BOperand count (a1+144)
+0x094--+0x110Explicit operand mapping table (pairs of index + bit position)
+0x19432BExtended operand attributes (from xmmword tables)
+0x1D4--+0x21464BConstant buffer slot table (16 DWORD slots, cleared to 0xFF by sub_7B9D30)
+0x2144BConstant buffer slot counter (a1+532)
+0x2188BEncoding validation context pointer (a1+536)
+0x2208BInstruction bits [63:0] (a1+544)
+0x2288BInstruction bits [127:64] (a1+552)
+0x230+Additional encoding space (up to 1280 bits total)

Instruction Word Format

SASS instructions use a 3-level opcode hierarchy packed into the first 32 bits of the encoding buffer. The format code in bits [0:3] determines instruction width:

128-bit instruction word:
  bits [0:3]     = 0x2       (format code: 128-bit)
  bits [4:6]     = 0x0       (scheduling group slot 0)
  bits [8:16]    = MAJOR     (9-bit major opcode, 0x00-0x171)
  bits [17:24]   = MINOR     (8-bit minor opcode / variant)
  bits [25:31]   = SUBOP     (7-bit sub-opcode / format ID)
  bits [48+]     = MODIFIERS (format-dependent modifier fields)
  bits [132:134] = 0x0       (extended opcode flag, at offset 0x84)

64-bit instruction word:
  bits [0:3]     = 0x1       (format code: 64-bit)
  bits [4:6]     = 0x0       (scheduling group slot 0)
  bits [8:16]    = MAJOR     (9-bit major opcode)
  bits [17:24]   = MINOR     (8-bit minor opcode)
  bits [25:31]   = SUBOP     (7-bit sub-opcode)
  (no bit 132 extended flag -- only 5 initial sub_7B9B80 calls)

256-bit instruction word:
  bits [0:3]     = 0x8       (format code: 256-bit)
  (used for IMAD.WIDE variants with 16 constant-bank operand slots)

The 128-bit format uses 6 initial sub_7B9B80 calls (including one at offset 0x84 for the extended flag). The 64-bit format uses only 5 (no 0x84 call). This is the reliable way to distinguish the two during analysis.

The maximum variant value observed is 0x2F (47 decimal), so each major opcode can have up to 48 sub-operations, though most have far fewer.

Encoder Template

Every encoding handler function follows an identical 10-phase template. The only differences between the approximately 1,086 encoder functions for SM100 are the specific constant values and which modifier-encoding helpers are called. This is textbook C++ template/macro expansion:

int64_t encode_OPCODE_VARIANT(int64_t a1, int64_t a2) {
    // a1 = instruction encoding context (output)
    // a2 = Ori IR instruction node (input)

    // Phase 1: Set instruction format header
    sub_7B9B80(a1, 0, 4, FORMAT_CODE);     // bits[0:3] = 1 (64b) / 2 (128b) / 8 (256b)
    sub_7B9B80(a1, 4, 3, 0);              // bits[4:6] = sched group slot 0
    sub_7B9B80(a1, 0x84, 3, 0);           // bits[132:134] = extended flag (128-bit only)
    sub_7B9B80(a1, 8, 9, OPCODE_CLASS);   // bits[8:16] = major opcode
    sub_7B9B80(a1, 0x11, 8, OPCODE_MINOR); // bits[17:24] = minor opcode / variant
    sub_7B9B80(a1, 0x19, 7, FORMAT_ID);   // bits[25:31] = sub-opcode / format ID

    // Phase 2: Load operand format descriptor
    *(xmmword*)(a1 + 8) = xmmword_23FXXXX; // 128-bit format field layout from rodata
    // Copy 3 arrays of 10 DWORDs into a1+24..a1+140 (slot sizes, types, flags)

    // Phase 3: Set operand count and modifier table
    *(int*)(a1 + 144) = NUM_SOURCE_OPERANDS;
    *(xmmword*)(a1 + 404) = xmmword_YYYYYYY; // modifier descriptor table

    // Phase 4: Initialize encoding context
    sub_7B9D30(a1);         // clear constant buffer slot table (memset +468, 0xFF, 64)
    sub_7B9D60(a1, a2, 0);  // encode reuse flags + guard predicate

    // Phase 5: Encode primary opcode ID
    void* ctx = *(void**)(a1 + 536);
    int opcode = sub_10BFxxx(*(a2+32) + 32 * *(a2+40));  // extract from IR operand
    int encoded = sub_10B6180(ctx, opcode);                // map through lookup table
    sub_7B9B80(a1, 8 * *(a1+16), 1, encoded);             // insert at computed position

    // Phase 6: Encode source operands (variable number and types)
    sub_7BC030(a1, a2, 0, 0x60);  // register operand 0 at bit offset 0x60
    sub_7BC030(a1, a2, 1, 0x70);  // register operand 1 at bit offset 0x70
    sub_7BCF00(a1, a2, 2, 0x88);  // immediate operand 2 at bit offset 0x88
    sub_7BC5C0(a1, a2, 3, 0x98);  // predicate operand 3 at bit offset 0x98

    // Phase 7: Encode instruction-specific modifiers
    int mod_val = sub_10B96A0(a2);              // read modifier from IR node
    int enc_mod = sub_10B3680(ctx, mod_val);    // validate and map
    *(int64_t*)(a1+544) |= ((int64_t)enc_mod << 55) & 0x180000000000000LL;

    // Phase 8: Encode explicit operand mapping (source operands with data offsets)
    *(int*)(a1 + 148) = operand_index;
    *(int*)(a1 + 152) = 8 * bit_position;
}

Operand Type Encoders

Four type-specific helper functions encode operands into the instruction word. Each reads the operand descriptor from the IR instruction's operand table at *(a2+32) + 32*operand_index.

Register Operand Encoder -- sub_7BC030

814 bytes, 6,147 callers. Encodes a general-purpose register (R0-R255, UR0-UR63):

// sub_7BC030(insn, ir_insn, operand_index, bit_offset)
void encode_register(int64_t a1, int64_t a2, int op_idx, int bit_off) {
    if (op_idx >= *(int*)(a2 + 92))  // check operand count
        return;
    void* operand = *(void**)(a2 + 32) + 32 * op_idx;
    int reg_type_raw = *(int*)(operand + 20);

    // Map register file type to 4-bit encoding:
    //   1->0, 2->1, 3->2, 4->3, 5->4, 6->5, 7->6, 8->7,
    //   16->8, 32->9, 64->10, 128->11
    int reg_type = map_regfile(reg_type_raw);
    int reg_num = *(int*)(operand + 4);  // signed register number

    sub_7B9B80(a1, bit_off, 1, 1);           // 1-bit presence flag
    sub_7B9B80(a1, bit_off + 1, 4, reg_type); // 4-bit register type
    sub_7B9B80(a1, bit_off + 6, 10, reg_num); // 10-bit register number
}

The register file type encoding maps raw operand type codes to a 4-bit hardware register file selector. The 12 supported values (1 through 128 in powers of 2) cover GPR, uniform registers, predicate registers, special registers, and extended register files.

Immediate / Constant-Buffer Encoder -- sub_7BCF00

856 bytes, 1,657 callers. Encodes immediate values and constant memory references (c[bank][offset]):

// sub_7BCF00(insn, ir_insn, operand_index, bit_offset)
void encode_immediate(int64_t a1, int64_t a2, int op_idx, int bit_off) {
    void* operand = *(void**)(a2 + 32) + 32 * op_idx;
    int type = *(uint8_t*)operand;

    if (type == 14 || type == 15 || type == 16) {
        // Predicate-typed immediate: store to constant buffer slot table
        *(void**)(a1 + 468 + 8 * *(int*)(a1 + 532)) = operand + 8;
        *(int*)(a1 + 532) += 1;
    }
    sub_7B9B80(a1, bit_off, 1, 1);               // presence flag
    sub_7B9B80(a1, bit_off + 11, 5, *(int*)(operand + 4)); // 5-bit value
}

Predicate Encoder -- sub_7BC5C0

416 bytes, 1,449 callers. Encodes predicate register operands (PT, P0-P6):

// sub_7BC5C0(insn, ir_insn, operand_index, bit_offset)
void encode_predicate(int64_t a1, int64_t a2, int op_idx, int bit_off) {
    void* operand = *(void**)(a2 + 32) + 32 * op_idx;
    sub_7B9B80(a1, bit_off, 2, pred_type);       // 2-bit predicate type
    sub_7B9B80(a1, bit_off + 3, 3, pred_cond);   // 3-bit condition code
    sub_7B9B80(a1, bit_off + 8, 8, pred_value);  // 8-bit predicate value
}

Uniform Register Encoder -- sub_7BC360

Used for uniform registers (UR0-UR63) and source operands with alternative bitfield layouts. 126 calls in the SM100 encoding range. Likely handles the UR register file which has a separate encoding namespace from the main GPR file.

Instruction Format Groups

The encoder functions are organized into 16 format groups, identified by the xmmword constant loaded at a1+8. Each xmmword holds the field layout descriptor for that instruction format. The groups divide into two categories:

128-bit Formats (11 groups)

Format DescriptorFormat IDEncoder CountDescription
xmmword_23F1DF80x03145General ALU/memory -- most common format
xmmword_23F29A80x19117Extended format for complex instructions
xmmword_23F21B00x0A99Multi-source ALU operations
xmmword_23F26780x1327Tensor/extended ALU
xmmword_23F20180x079Miscellaneous ALU
xmmword_23F23480x0D6Specialized ALU
xmmword_23F2EF80x235Extended variant
xmmword_23F28100x164Bulk data / DMA
xmmword_23F21280x092Rare format
xmmword_23F2DE80x212Rare extended
xmmword_23F25F00x122Rare format

64-bit Formats (5 groups)

Format DescriptorEncoder CountDescription
xmmword_23F1F08113Short-form general -- widest opcode coverage (27 classes)
xmmword_23F1D7041Short-form 4-operand
xmmword_23F1F9011Short-form variant
xmmword_23F22388Short-form variant
xmmword_23F2C501Minimal format

The 128-bit group (format code 2) encodes long-form SASS instructions (ALU, load/store, texture, tensor core). The 64-bit group (format code 1) encodes short-form instructions (simple moves, branches, barriers, NOP-like control). Two additional functions use format code 8 (256-bit) for IMAD.WIDE variants with 16 constant-bank operand slots.

Instruction Format Group Catalog

Format Descriptor Architecture

Each format group is defined by a 128-bit xmmword constant stored in rodata at addresses 0x23F1xxx--0x23F2xxx. This descriptor is loaded via SSE into the encoding context at a1+8:

*(__m128i *)(a1 + 8) = _mm_loadu_si128(&xmmword_23F29A8);

Immediately following each xmmword in rodata are three arrays of 10 DWORDs that define the operand slot layout. The encoder copies these into the context object at a1+24 through a1+140:

Rodata ArrayContext OffsetContent
dword_XXXXX8[10]a1+24 .. a1+60Operand slot sizes (bits per slot)
dword_XXXXE0[10]a1+64 .. a1+100Operand slot types (register class selector)
dword_XXXXX8[10]a1+104 .. a1+140Operand slot flags (encoding mode flags)

Observed slot-size values: 10 = register (10-bit number + overhead), 12 = register with type, 17 = immediate/cbuf, -1 = unused. Slot-type values: 28 = register-type, 0 = basic, -1 = unused. Slot-flag values: 0 = default, 2 = secondary (uniform/extended), -1 = unused.

The copy uses SSE aligned loads for 16-byte chunks and scalar DWORD stores for remainders. The alignment check visible in every decompiled encoder (if (a1 + 120 <= dword_XXXXX8 || a1 + 24 >= &dword_XXXXX8)) is a compiler-generated overlap guard for the memcpy-like bulk copy.

Bitfield Packer Detail -- sub_7B9B80

The core encoding primitive. 216 bytes compiled, 18,347 callers total. Inserts an arbitrary-width bitfield into the 1280-bit buffer at a1+544:

// sub_7B9B80(a1, bit_offset, bit_width, value)
// Reconstructed algorithm from decompiled code:
__int64 bitfield_insert(__int64 a1, uint32_t bit_offset, int bit_width, uint64_t value) {
    uint32_t end = bit_offset + bit_width;
    uint32_t neg_base = -64 - bit_offset;  // pre-computed right-shift amount
    uint32_t pos = 0;
    do {
        while (1) {
            uint32_t chunk_end = pos + 64;
            if (bit_offset > pos + 63 || end <= pos) goto next;  // no overlap

            uint32_t start = (bit_offset >= pos) ? bit_offset : pos;
            uint32_t stop  = (end <= chunk_end) ? end : chunk_end;
            int width = stop - start;
            int shift_right = (chunk_end + neg_base < 0) ? 0 : chunk_end + neg_base;
            int bit_in_qword = start & 0x3F;
            __int64 *qword = (__int64*)(a1 + 8 * (start >> 6) + 544);
            uint64_t shifted = value >> shift_right;

            if (width == 64)
                *qword |= shifted << bit_in_qword;
            else
                *qword |= (shifted & ~(-1ULL << width)) << bit_in_qword;
        next:
            pos = chunk_end;
            if (chunk_end == 1280) return pos;
        }
    } while (pos != 1280);
    return pos;
}

Key properties:

  • Handles cross-QWORD-boundary fields: a 9-bit opcode starting at bit 59 writes 5 bits to QWORD 0 and 4 bits to QWORD 1
  • Loop terminates at bit position 1280 (20 QWORDs), hard ceiling
  • For typical field widths (1--9 bits), executes 1--2 iterations
  • Called 8--12 times per encoder function (average ~10)
  • The 256-bit format encoders call it with wider fields (up to 32 bits for data values)

128-bit Format 0x03 -- General ALU/Memory (145 encoders)

The most populous format group. Handles the bread-and-butter ALU and memory instructions.

PropertyValue
Descriptorxmmword_23F1DF8
Format ID0x03 (bits[25:31])
Slot arraysdword_23F1E08, dword_23F1E30, dword_23F1E40
Operand slots2--7 per instruction
Typical pattern3 reg + 1 imm + 1 pred (5 slots)
Modifier fields4--8 per instruction

Opcode classes (29): 0x08, 0x0B, 0x0F, 0x10, 0x16, 0x17, 0x19, 0x1A, 0x1B, 0x20, 0x22, 0x25, 0x28, 0x2A, 0x2B, 0x30, 0x32, 0x34, 0x35, 0x36, 0x37, 0x38, 0x3B, 0x41, 0x45, 0x4A, 0x4B, 0x5B, 0x67.

128-bit Format 0x19 -- Extended Complex (117 encoders)

Second most common. Used for instructions with rich modifier fields or unusual operand configurations.

PropertyValue
Descriptorxmmword_23F29A8
Format ID0x19 (bits[25:31])
Slot arraysdword_23F29B8, dword_23F29E0, dword_23F2A08
Operand slots3--6 per instruction
Modifier fields5--8 per instruction

Opcode classes (8): 0x0F, 0x10, 0x1A, 0x1B, 0x22, 0x38, 0x4D, 0x5E. Notable concentration: opcode 0x1B has 41 variants in this format alone (tensor/MMA family); opcode 0x5E has 26 variants. The load/store family (0x38) uses this format for 7 of its 16 variants -- the ones with extended addressing modes.

128-bit Format 0x0A -- Multi-Source ALU (99 encoders)

Designed for instructions with 4--7 source operands. Heavily weighted toward rich ALU operations.

PropertyValue
Descriptorxmmword_23F21B0
Format ID0x0A (bits[25:31])
Operand slots4--7 per instruction
Typical pattern4 reg + 1 imm + 1 pred

Opcode classes (10): 0x10, 0x16, 0x17, 0x20, 0x25, 0x28, 0x2A, 0x45, 0x4B, 0x67. Opcode 0x2A dominates with 30 variants; opcode 0x25 has 18.

128-bit Format 0x13 -- Tensor/Extended ALU (27 encoders)

Contains the most complex encoders in the binary. Opcode 0x5A variant 0x02 (sub_D89C90, 2015 bytes) has 18 modifier fields -- the maximum observed.

PropertyValue
Descriptorxmmword_23F2678
Format ID0x13 (bits[25:31])
Slot arraysdword_23F2688, dword_23F26B0, dword_23F26D8
Operand slots4--7 per instruction
Modifier fields8--18 per instruction

Opcode classes (7): 0x10, 0x16, 0x17, 0x1A, 0x41, 0x5A, 0x67.

128-bit Formats 0x07, 0x0D, 0x23, 0x16, 0x09, 0x21, 0x12 -- Rare Formats (35 encoders combined)

DescriptorFormat IDEncodersOpcode Classes
xmmword_23F20180x0790x0F, 0x10
xmmword_23F23480x0D60x0F, 0x16, 0x67
xmmword_23F2EF80x2350x10
xmmword_23F28100x1640x4B (bulk/DMA)
xmmword_23F21280x092--
xmmword_23F2DE80x212--
xmmword_23F25F00x1220x4B

Format 0x16 and 0x12 share opcode class 0x4B, suggesting they encode different addressing-mode variants of the same bulk-data instruction.

64-bit Format A (xmmword_23F1F08) -- Short-Form General (113 encoders)

Widest opcode coverage of any single format. Covers 27 distinct opcode classes with few variants each -- the simple, common instructions.

PropertyValue
Descriptorxmmword_23F1F08
Operand slots0--3 per instruction
Register offsets0x40, 0x50, 0x60, 0x70

Opcode classes (27): 0x00--0x09, 0x0A--0x0F, 0x10, 0x11, 0x12, 0x14, 0x16, 0x1B, 0x1C, 0x20, 0x21, 0x23, 0x25. Many of these are NOP/control, simple moves, and compact branches.

64-bit Format B (xmmword_23F1D70) -- Short-Form 4-Operand (41 encoders)

Bimodal operand count: either 0 operands (control instructions) or 4 operands (compact arithmetic with all-register sources).

Opcode classes: 0x00--0x09, 0x10, 0x12, 0x14--0x1E, 0x26, 0x28, 0x2A.

64-bit Formats C, D, E -- Specialized Short Forms (20 encoders combined)

DescriptorEncodersNotes
xmmword_23F1F9011Short-form variant C
xmmword_23F22388Short-form variant D
xmmword_23F2C501Minimal format, single encoder; also appears in 128-bit category with 0 uses

Distinguishing 64-bit vs 128-bit Encoders

The 128-bit format sets the extended opcode flag at bit offset 0x84, which the 64-bit format does not:

128-bit (6 initial sub_7B9B80 calls):
  sub_7B9B80(a1, 0,    4, 2)     // format code = 2
  sub_7B9B80(a1, 4,    3, 0)     // sched group slot
  sub_7B9B80(a1, 0x84, 3, 0)    // extended flag at bit 132  <-- PRESENT
  sub_7B9B80(a1, 8,    9, MAJ)   // major opcode
  sub_7B9B80(a1, 0x11, 8, MIN)   // minor opcode
  sub_7B9B80(a1, 0x19, 7, FID)   // format ID

64-bit (5 initial sub_7B9B80 calls):
  sub_7B9B80(a1, 0,    4, 1)     // format code = 1
  sub_7B9B80(a1, 4,    3, 0)     // sched group slot
                                  // NO 0x84 call           <-- ABSENT
  sub_7B9B80(a1, 8,    9, MAJ)   // major opcode
  sub_7B9B80(a1, 0x11, 8, MIN)   // minor opcode
  sub_7B9B80(a1, 0x19, 7, FID)   // format ID

The 256-bit format (format code 8) is used by exactly 2 encoders for IMAD.WIDE (major 0x59, minor 0x02 and 0x03), each with 16 constant-buffer operand slots encoded via sub_7BCF00.

Dispatch Tables -- The Six Megafunctions

Six switch-dispatch megafunctions in the 0x10C0B20--0x10E32E0 range form the central routing logic of the instruction codec. All six switch on the opcode category at *(WORD*)(a1+12) with up to 370 cases (0x0 through 0x171), each containing sub-switches on field ID:

FunctionSizeDecompiled LinesCallersPurpose
sub_10C0B20180 KB9,2313,109setField -- write a value into a named field
sub_10D5E60197 KB6,491961getFieldOffset -- return bit-offset of a named field
sub_10E32E0187 KB6,24072hasField -- boolean: does this instruction have field X?
sub_10CCD80142 KB7,5814setFieldDefault -- write hardcoded default for a field
sub_10CAD7068 KB1,86474getOperandFieldOffset -- bit-offset of a per-operand field
sub_10C769065 KB2,313288setOperandField -- write a per-operand field value

sub_10C0B20 (setField) is one of the most-called functions in the entire ptxas binary at 3,109 call sites. It writes field values using sub_AF80xx writer stubs and contains jump-out targets (0xAF43F0, 0xAF44C0, 0xAF4550) for bit-manipulation operations that cross word boundaries.

sub_10D5E60 (getFieldOffset) returns extractor(a1+48, bit_position) + base_offset for each field, where base_offset is a field-specific constant (observed values: +125, +790, +1278, etc.). Returns 0xFFFFFFFF when the queried field does not exist in the given instruction category.

sub_10CAD70 (getOperandFieldOffset) operates on per-operand packed records at *(QWORD*)(a1+32) + 32*operand_index + 24. The field IDs it handles include: 1 (register class), 2 (operand type), 7, 8, 12 (operand size), 13 (address mode), 14, 15, 19, 24, 25, 26, 27, 29.

Dead cases (opcode categories without the queried field) share a common pattern: cases 3, 0x11, 0x24, 0x26, 0x2D, 0x75, 0x78, 0x84, 0x8C-0x9F, 0xA1-0xBA, 0xBE-0x16F return 0xFFFFFFFF or false.

Bitfield Accessor Library

The 0x10B0000--0x10BF2C0 range contains 2,095 machine-generated bitfield read/write primitives for the 192-bit packed instruction format. These are the building blocks that the six megafunctions call:

  • 1,661 functions under 200 bytes: pure getters/setters for individual fields
  • 412 functions between 200-500 bytes: multi-field accessors
  • 22 functions above 500 bytes: complex accessors with validation

Seven core extractors handle all bitfield reads:

FunctionWidthStorage Format
sub_10B28E01-bit192-bit (3x QWORD)
sub_10B28602-bit192-bit
sub_10B27E03-bit192-bit
sub_10B27604-bit192-bit
sub_10B26E05-bit192-bit
sub_10B26502-bit32-bit array
sub_10B25C03-bit32-bit array

The 192-bit format (3 QWORDs = 24 bytes, stored at a1+48) handles boundary crossing: if a bitfield spans a QWORD boundary, the extractor combines partial reads from adjacent words. The 32-bit-array format is used for sub-fields that are naturally DWORD-aligned.

A typical accessor is trivially simple:

// sub_10BEF80 (140 bytes)
int get_field_X(int64_t a1) {
    return (*(uint32_t*)(a1 + 24) & 3) + 51;  // extract 2-bit field, add base
}

Modifier Encoding

After operands are encoded, each handler packs instruction-specific modifier fields into the bits at a1+544 (primary word) and a1+552 (extended word). The pattern is:

  1. Read modifier value from IR node via a property extractor (sub_10B9xxx family)
  2. Validate and map through an encoding lookup table (sub_10B3xxx/sub_10B4xxx family)
  3. OR the result into the packed word at a shifted/masked position

The most commonly used modifier-encoding functions:

FunctionCallersBitsLikely Meaning
sub_10B61808,0911Boolean flag (.S, .STRONG, .SAT, etc.)
sub_10B61602,2051Boolean flag (.NEG, .ABS, etc.)
sub_10B61401,6451Boolean flag variant
sub_10B2D905382Data type, rounding mode
sub_10B55804755Shift amount, cache policy
sub_10B44E04162Addressing mode
sub_10B62203633Register bank, cache level
sub_10B46503304Type qualifier, address mode
sub_10B47F02434Type qualifier variant
sub_10B2F0015133-bit modifier field
sub_10B2F2010144-bit modifier field

Modifier fields per instruction range from 0 (simple control instructions) to 18 (the most complex encoder, sub_D89C90 for opcode class 0x5A). The average is approximately 6 modifier fields per encoder. Bit positions in a1+544 concentrate in bits 48-63; bit positions in a1+552 concentrate in bits 0-11.

Physical Register Encoding

The SASS instruction encoder uses a two-stage pipeline to convert abstract virtual registers into hardware register fields in the final instruction word. The first stage (Ori encoding, described above in "Register Operand Encoder") packs register type and number into operand slots within the 1280-bit encoding buffer. The second stage (SASS emission) maps the compiler's abstract (register_class, sub_index) pair into an 8-bit hardware register number and writes it into the final 128-bit instruction word. This second stage is implemented by the register-class encoding tables at address range 0x1B4C000--0x1B76000 (Zone A of the emission backend).

Class-to-Hardware Formula

sub_1B6B250 (2965 bytes, 254 callers, 0 callees) is a fully unrolled lookup table that implements the mapping:

hardware_reg = register_class * 32 + sub_index

The function takes two integer arguments (a1, a2) where a1 is the register class (0--5) and a2 is the sub-register index within that class. It is compiled as a deeply nested if-chain covering all 156 valid (class, index) combinations. The decompiler output is 495 lines of cascading conditionals, but every return value satisfies the formula a1 * 32 + a2 exactly:

// sub_1B6B250 -- reconstructed from decompiled lookup table
__int64 register_class_to_hardware(int reg_class, int sub_index) {
    // Returns reg_class * 32 + sub_index for all valid inputs.
    // Valid classes: 0, 1, 2, 3, 4, 5
    // Valid sub-indices: 1..15, 17..27 (index 0 and 16 excluded)
    // Returns 0 for any unmatched input (fallthrough).
}

The guard wrapper sub_1B73060 (19 bytes, 483 callers) short-circuits the no-register case:

// sub_1B73060 -- guard wrapper
__int64 encode_register_guarded(__int64 ctx, int reg_class, int sub_index) {
    if (reg_class | sub_index)
        return register_class_to_hardware(reg_class, sub_index);
    else
        return 0;  // no register
}

Per-Class Hardware Number Ranges

Each class occupies a 32-number stride in the hardware register namespace. Within each stride, indices 1--15 and 17--27 are populated (26 registers per class). Index 0 maps to the no-register sentinel via the guard wrapper. Index 16 is absent from the lookup table -- a gap in every class.

Classa1Hardware RangePopulated IndicesGapLikely Register File
000--271--15, 17--2716R (GPR primary)
1132--591--15, 17--2748R (GPR secondary)
2264--911--15, 17--2780P (predicate)
3396--1231--15, 17--27112UR (uniform GPR)
44128--1551--15, 17--27144UR (uniform ext)
55160--1871--15, 17--27176P/UP (uniform pred)

Hardware numbers 28--31 (and the corresponding padding in each class) are unused, providing alignment to 32-register boundaries. The maximum hardware register number produced by the table is 187 (class 5, index 27). The 8-bit encoding field can represent 0--255, so values 188--255 are reserved.

The index-16 gap in every class is consistent across all 6 classes. This likely corresponds to a hardware-reserved slot or a register numbering convention where physical register class*32+16 has special semantics (potentially a sentinel or a register-file-boundary marker).

Split Bitfield Writer

sub_1B72F60 (32 bytes, 483 callers) writes the 8-bit hardware register number into the SASS instruction word. The encoding is split across two non-contiguous bitfields within a single DWORD:

// sub_1B72F60 -- register field writer (decompiled verbatim)
__int64 write_register_field(__int64 a1, int encoded_reg) {
    __int64 buf = *(_QWORD *)(a1 + 112);   // instruction encoding buffer
    __int64 result = *(_DWORD *)(buf + 12)  // DWORD at byte offset 12
                   | ((_WORD)encoded_reg << 9) & 0x3E00u;     // low 5 bits -> [13:9]
    *(_DWORD *)(buf + 12) = result
                   | (encoded_reg << 21) & 0x1C000000;        // high 3 bits -> [28:26]
    return result;
}

Bit-level layout within the DWORD at *(instruction_buffer + 12):

DWORD bits:  31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7 ..  0
                      [  h2:h0  ]                                      [ l4:l3:l2:l1:l0 ]
                      hw[7:5]                                          hw[4:0]

The DWORD at byte offset 12 covers bits [127:96] of the 128-bit instruction word. In full instruction coordinates:

FieldDWORD BitsInstruction BitsWidthContent
Low[13:9][109:105]5 bitshardware_reg[4:0]
High[28:26][124:122]3 bitshardware_reg[7:5]

The 12-bit gap between instruction bits [121] and [110] is occupied by other instruction fields (modifiers, flags, secondary operand encodings). This split-field design is common in GPU ISAs where instruction bits are at a premium and different fields must be routed to different functional unit inputs.

sub_1B72FE0 (32 bytes, 104 callers) is byte-identical to sub_1B72F60 but occupies a different vtable slot, used by a secondary operand encoding path.

Extended Register Encoder

sub_1B6EA20 (7194 bytes, 25 callers) extends the base encoding with operand modifier support. It takes 5 parameters:

// sub_1B6EA20 -- register encoding with modifiers
__int64 encode_register_with_modifiers(
    int reg_class,      // a1: register class (0-5)
    int sub_index,      // a2: sub-register index
    int negation,       // a3: .NEG modifier flag
    int abs_value,      // a4: |.ABS| modifier flag
    int type_modifier   // a5: type cast modifier
);

When all modifier flags are zero (a3 | a4 | a5 == 0), the function returns the same value as sub_1B6B250 -- the base class * 32 + index result. When modifiers are present, the function continues into extended encoding logic that packs modifier bits alongside the register number. The guard wrapper sub_1B748C0 (35 bytes, 104 callers) provides the same no-register short-circuit for the extended variant.

Additional encoding variants for different operand positions include sub_1B6D590, sub_1B70640, sub_1B71AD0, sub_1B748F0, and sub_1B76100 (5264--6106 bytes each, 2--49 callers each). All share the same nested-if structural pattern and operate on the same class/index domain.

Encoding Pipeline Summary

The complete register encoding pipeline from virtual register to instruction bits:

Virtual Register (vreg+64 = reg_type, vreg+68 = physical_reg)
  |
  v
[Ori Encoder -- sub_7BC030, 6147 callers]
  Reads: operand+20 (reg_type_raw), operand+4 (reg_num)
  Writes: 1-bit presence + 4-bit type + 10-bit number into 1280-bit buffer
  |
  v
[SASS Emission -- sub_1B6B250 via sub_1B73060, 483 callers]
  Input: (register_class, sub_index)
  Formula: hardware_reg = class * 32 + sub_index
  Output: 8-bit hardware register number (0-187)
  |
  v
[Bitfield Writer -- sub_1B72F60, 483 callers]
  Input: 8-bit hardware register number
  Output: split across instruction bits [109:105] and [124:122]

Zone A Function Map

FunctionSizeCallersRoleConfidence
sub_1B6B2502,965 B254Core class*32+index lookup tableHIGH
sub_1B6EA207,194 B25Extended encoding with modifier bitsHIGH
sub_1B7306019 B483Guard wrapper for sub_1B6B250CERTAIN
sub_1B748C035 B104Guard wrapper for sub_1B70640CERTAIN
sub_1B72F6032 B483Split bitfield register writerHIGH
sub_1B72FE032 B104Identical writer (different vtable slot)HIGH
sub_1B730806,106 B883-operand register encoding (class, index, modifier)HIGH
sub_1B6D5905,264 BvariesRegister encoding variant (operand position A)HIGH
sub_1B70640variesvariesRegister encoding variant (operand position B)HIGH
sub_1B71AD0variesvariesRegister encoding variant (operand position C)HIGH
sub_1B748F0variesvariesRegister encoding variant (operand position D)HIGH
sub_1B76100variesvariesRegister encoding variant (operand position E)HIGH

Decoder Functions

97 decoder functions in the 0xEB3040--0xED0FE0 range reverse the encoding: they extract operand information from packed SASS bitfields back into Ori IR representation. The decoder entry point is sub_EB3040, a dispatcher that performs binary search on the instruction type word (*(a2+12), *(a2+14), *(a2+15)) against a table at off_22E6380. For instruction types 120/121, it falls through to the generic decoder sub_7BFAE0.

The decoder template mirrors the encoder but in reverse:

void decode_OPCODE(int64_t a1, int64_t a2) {
    // 1. Set output instruction type
    *(uint16_t*)(a2 + 12) = INSTR_TYPE_ID;

    // 2. Load operand format table (same xmmword constants as encoder)
    // 3. Set operand count
    *(int*)(a1 + 144) = NUM_OPERANDS;

    // 4. Decode operands using type-specific decoders
    sub_7BD3C0(a1, a2, 0, 0x50, 2);   // GPR register (type=2)
    sub_7BE090(a1, a2, 1, 0x60, 3);   // predicate register (type=3)
    sub_7BD650(a1, a2, 2, 0x70, 10);  // extended register (type=10)

    // 5. Extract control bits (reuse flags, stall counts, yield hints)
    sub_7BD260(a1, a2);

    // 6. Translate encoded values back to IR references
    int reg = sub_AF7DF0(*(void**)(a1+536), extracted_bits);
    sub_B056B0(dest_ptr, reg);
    int pred = sub_AF7200(*(void**)(a1+536), pred_bits);
    sub_AFA380(a2, pred);

    // 7. Extract modifier bitfields (reverse of encoder phase 7)
    sub_AF53B0(*(void**)(a1+536), *(a1+550) & mask);
    sub_AFCEB0();  // commit extracted value
}

Decoder operand count distribution: 6 two-operand, 18 three-operand, 22 four-operand, 16 five-operand, 22 six-operand, 12 eight-operand decoders.

Opcode ID Extractors

Over 100 small functions in the 0x10BF000--0x10C0C00 range serve as opcode discriminators. Each maps an IR instruction node to an opcode ID by reading fields from the operand table. The most-used extractors:

FunctionEncoder UsersMajor Opcode Family
sub_10BF44048Generic (most common)
sub_10BF23045Generic
sub_10BF59043Generic
sub_10BFA90300x59 (IMAD variants)
sub_10BFD30260xFD family
sub_10BFFA0250x4F family
sub_10BF580230x29 (IADD/IADD3)
sub_10BF680160x38 (load/store)
sub_10C0AF0140xDF (WGMMA)

89 distinct opcode reader functions cover all instruction families.

Per-SM Architecture Encoding

The encoding system is replicated per SM target. Each SM architecture has its own set of encoder/decoder functions with different xmmword opcode constants. The SM100 (Blackwell datacenter) implementation spans these address ranges:

RangeFunctionsLayer
0xD27000--0xDFC000592Encoder stubs (p1.12)
0xDFC000--0xEB2AE0494Encoder stubs continuation (p1.13)
0xEB3040--0xED0FE097Decoder functions (p1.13)
0x107B1E0--0x10AD700641Encoder stubs continuation (p1.16)
0x10ADD30--0x10AFF8078Instruction lifecycle & scheduling
0x10B0000--0x10BF2C02,095Bitfield accessor library (p1.16)
0x10C0B20--0x10E32E0184Dispatch table megafunctions (p1.16)
0x10EE900--0x1134160~400Binary encoders: IR fields to bits (p1.16)
0x1134160--0x114F380~132High-level encode path (p1.16)

The total SM100 codec spans roughly 2.5 MB of binary code across approximately 4,700 functions (including the shared bitfield accessor library).

Other SM targets (SM75 Turing, SM80 Ampere, SM86 Ada, SM89 Lovelace, SM90a Hopper, SM103 Blackwell Ultra, SM120 consumer Blackwell) have parallel encoder populations in the p1.14, p1.15, p1.17--p1.22 address ranges, each with matched xmmword constants for their architecture-specific instruction set.

Per-SM Instruction Format Descriptors

316 instruction format descriptor functions at 0x1732170--0x17A9B70 form the shared, architecture-neutral instruction pattern database. Unlike the per-SM encoder stubs (replicated per architecture at separate address ranges), these descriptors are a single set of functions that describe every SASS opcode variant's encoding geometry: bitfield layout, operand slot configuration, and modifier schema. They are invoked exclusively through virtual dispatch (zero static callers) from the ISel passes (sub_A4BC60, sub_A4D3F0) via the FNV-1a hash-based instruction matcher at sub_1731440.

Descriptor Template

Every descriptor function initializes an Encoding Context object through a fixed 4-phase sequence:

// Phase 1: Opcode header (5 calls for 64-bit, 6 for 128-bit)
sub_7B9B80(a1, 0,    4, FORMAT_CODE);   // bits[3:0]     format: 1=64b, 2=128b
sub_7B9B80(a1, 4,    3, 0);             // bits[6:4]     sched group slot
sub_7B9B80(a1, 0x84, 3, 0);             // bits[134:132] ext flag (128-bit ONLY)
sub_7B9B80(a1, 8,    9, MAJOR_OP);      // bits[16:8]    9-bit major opcode
sub_7B9B80(a1, 0x11, 8, MINOR_OP);      // bits[24:17]   8-bit minor opcode
sub_7B9B80(a1, 0x19, 7, FORMAT_ID);     // bits[31:25]   7-bit format ID

// Phase 2: Format layout descriptor (Tier 1) -- selects operand geometry
*(__m128i*)(a1 + 8) = xmmword_23FXXXX;  // 128-bit format template from rodata
// + bulk copy of 3 x 10 DWORD arrays into a1+24..a1+140

// Phase 3: Architecture modifier table (Tier 2) -- selects per-SM encoding
*(__m128i*)(a1 + 404) = xmmword_YYYYYYY;  // per-SM modifier constants
*(DWORD*)(a1 + 420) = VAL1;               // explicit modifier overrides
*(DWORD*)(a1 + 424) = VAL2;

// Phase 4: Operand count + standard encoding tail
*(DWORD*)(a1 + 144) = NUM_OPERANDS;       // 0--7
sub_7B9D30(a1);                            // clear constant buffer table
sub_7B9D60(a1, a2, 0);                     // encode reuse + guard predicate
// Then: opcode extraction, register encoding, modifier field packing

Two-Tier xmmword Architecture

Each descriptor loads two classes of xmmword constants that together fully specify the instruction encoding:

Tier 1 (at a1+8): Format Layout Descriptor. Selects the instruction format -- operand slot sizes, types, and field layout. These are the 16 format groups documented in the "Instruction Format Group Catalog" section above. Addresses in the 0x23F1xxx--0x23F2xxx rodata range.

Tier 2 (at a1+404): Architecture Modifier Table. Selects per-SM encoding variations for the same format layout. Two instructions with the same Tier 1 descriptor but targeting different architectures use different Tier 2 constants. Addresses span three rodata ranges:

Rodata RangeGroupFunctionsPaired With
0x202A280--0x202A2B0A~40202A290 or 202A2A0+202A2B0 at a1+420
0x22F1B30--0x22F1B50B/C~8None (single 16B block)
0x22F1BA0--0x22F1BB0D~3None
0x22F1AA0--0x22F1AE0E~3(observed in SM100 encoder range)
0x22F1C20--0x22F1C30F~2Paired at a1+404/a1+420
0x23B2DE0G4None (rare/specialized)

SM Generation Mapping

The Tier 2 modifier groups correspond to GPU architecture generations. The mapping is inferred from operand table sizes (larger = newer), function counts per group (fewer = newer/specialized), and cross-reference with the per-SM encoder stubs at known address ranges:

Modifier AddressProbable SM RangeISA FamilyConfidence
0x202A280--0x202A2B0sm_50--sm_75Maxwell / Pascal / Volta / TuringMEDIUM
0x22F1B30--0x22F1B50sm_80--sm_86Ampere / AdaMEDIUM
0x22F1BA0--0x22F1BB0sm_89--sm_90aLovelace / HopperMEDIUM
0x22F1AA0--0x22F1AE0sm_100+Blackwell datacenterMEDIUM
0x22F1C20--0x22F1C30sm_103 / sm_120Blackwell Ultra / consumerLOW
0x23B2DE0Cross-archSpecialized / rare instructionsLOW

The progression from 0x202A to 0x22F1 to 0x23B2 in rodata address space mirrors the SM generation ordering. Group A (Maxwell--Turing) is the most populous, consistent with the longest-supported ISA family. Groups E and F have the fewest functions, consistent with the newest architectures that introduce fewer format changes.

Format Code Distribution

Format CodeInstruction WidthDescriptor Countsub_7B9B80 Header CallsNotes
164-bit~1205 (no 0x84 call)Simple moves, branches, barriers, NOP-like control
2128-bit~1946 (includes 0x84)ALU, load/store, texture, tensor core
8256-bit2ExtendedIMAD.WIDE with 16 constant-bank slots

Descriptor-Initialized Context Fields

The format descriptor writes these fields into the Encoding Context object. All offsets are decimal:

OffsetSizeInitialized ByContent
+816BPhase 2 (Tier 1 xmmword)Format layout descriptor
+24--+6040BPhase 2 (bulk copy)Operand slot sizes (10 DWORDs)
+64--+10040BPhase 2 (bulk copy)Operand slot types (10 DWORDs)
+104--+14040BPhase 2 (bulk copy)Operand slot flags (10 DWORDs)
+1444BPhase 4Operand count (0--7)
+40416BPhase 3 (Tier 2 xmmword)Architecture modifier table
+4204BPhase 3 (scalar)Architecture modifier field 1
+4244BPhase 3 (scalar)Architecture modifier field 2

Pipeline Position

The format descriptors bridge ISel pattern matching and per-SM encoding:

ISel Pattern Matcher (sub_1731440, FNV-1a hash on *(a2+12))
  |
  v  (virtual dispatch via vtable)
Format Descriptor (one of 316 at 0x1732170--0x17A9B70)
  Writes: a1+0..a1+144   (format layout + operand geometry)
  Writes: a1+404..a1+424  (architecture modifier table)
  |
  v  (encoding context passed down)
Per-SM Encoder Stub (e.g. 0xD27xxx for SM100)
  Reads: format context from descriptor
  Writes: a1+544..a1+703  (1280-bit encoding buffer)

Representative Examples

sub_1732170 -- 64-bit float conversion (single-dest):

FieldValueMeaning
Format code164-bit instruction
Major opcode0x0CFloat conversion family
Minor opcode0x0DVariant D
Format ID5Short-form general (23F1F08)
Tier 1xmmword_23F1F08Short-form general, 27 opcode classes
Tier 2xmmword_22F1B30Group B (Ampere/Ada)
Operand count3Register operands at 0x50, 0x60, 0x70
Modifier fields12Spanning a1+544 and a1+552

sub_1740200 -- 128-bit IMAD.WIDE (dual-dest):

FieldValueMeaning
Format code2128-bit instruction
Major opcode0x23IMAD.WIDE family
Minor opcode0x12Variant with modifier 0x13
Format ID0x13Tensor/extended ALU (23F2678)
Tier 1xmmword_23F2678Extended ALU, 7 opcode classes
Tier 2xmmword_202A280Group A (Maxwell--Turing)
Dual-destYes0x84 field present, set to 0

sub_1732E90 -- 128-bit extended complex:

FieldValueMeaning
Format code2128-bit instruction
Major opcode0x0CFloat conversion family
Minor opcode0x0CSame as major (self-referencing variant)
Format ID0x19Extended complex (23F29A8)
Tier 1xmmword_23F29A8Extended complex, 8 opcode classes
Tier 2xmmword_22F1B30Group B (Ampere/Ada)

Operand Encoding Patterns

The 576 encoder functions in the p1.12 range use 52 distinct operand encoding patterns. The most common:

Pattern (reg, imm, pred)CountDescription
3 reg + 1 pred88Standard 3-source with predicate
2 reg + 1 pred57Binary op with predicate
3 reg only43Ternary ALU, no predicate/immediate
3 reg + 1 imm + 1 pred42MAD-class with immediate + predicate
2 reg only40Simple binary
3 reg + 1 imm25Ternary with immediate
1 reg + 1 pred22Unary with predicate
4 reg + 1 imm21Quaternary with immediate
4 reg only20Quaternary register-only

Register operand bit offsets are format-dependent:

  • 64-bit format: 0x40, 0x50, 0x60, 0x70
  • 128-bit format: 0x60, 0x70, 0x88, 0x98, 0xA8

Major Opcode Summary (SM100)

102 unique major opcodes were identified across 494 encoding variants (p1.13 range alone). Opcode-to-mnemonic mapping is inferred from operand patterns and opcode density; exact mnemonic assignment requires correlation with ROT13-obfuscated instruction names found elsewhere in the binary.

Memory / Load-Store

MajorVariantsLikely SASS Mnemonics
0x3816LDG, STG, LDS, STS
0x602Extended load
0x70--0x729Load groups A/B/C
0xA4--0xA612Load/store with addressing modes
0xAD9Memory extended
0x1E4ATOM, ATOMS
0x99, 0xA22Extended atomics
0x392REDUX (reduction)

Integer Arithmetic

MajorVariantsLikely SASS Mnemonics
0x5930IMAD, IMAD.HI, IMAD.WIDE, ISCADD
0x2924IADD3, IADD3.64, IADD32I
0x4F25Extended integer operations
0x3B10Integer MUL/MAD extended

Floating Point

MajorVariantsLikely SASS Mnemonics
0x3A1Float operation
0x3E--0x404FFMA, FFMA variants
0x43--0x442Float MUL/MAD
0x4A4FADD, FMUL, FFMA forms
0x496HFMA2, HADD2, HMUL2
0x5C6HFMA2 variants
0x5F2Half-float extended

Tensor Core / WGMMA

MajorVariantsLikely SASS Mnemonics
0xA8--0xA916Tensor core A/B (WGMMA, HMMA)
0xAB--0xAC12Tensor core C/D
0xAE--0xB030Tensor core E/F/G
0xB1--0xB315Tensor core H/I/J
0xDF14WGMMA dispatch (main family)
0x124Matrix operations
0x546Extended matrix

Control Flow

MajorVariantsLikely SASS Mnemonics
0x1810BRA, SSY, CAL, EXIT, RET, BREAK, CONT
0x195Control flow group B
0x7D2YIELD, control
0x242BAR, barrier/sync
0xCF3BARRIER
0xD42BARRIER B
0x332DEPBAR

Comparison / Predicate

MajorVariantsLikely SASS Mnemonics
0x0D10ISETP, FSETP, DSETP
0x178PSETP, PLOP3
0x956Comparison variants

Data Movement / Conversion

MajorVariantsLikely SASS Mnemonics
0x615MOV, MOV.64, MOV32I
0x46, 0x66, 0x453MOV variants
0x566F2I, I2F, F2F type conversions
0x626Type conversion group 2
0x104SEL (conditional select)
0x1B3PRMT (permute)

Instruction Object Lifecycle

The instruction object constructor sub_10AFF80 (11 KB, 3 callers: sub_6F0A30, sub_6F52F0, sub_9EE390) takes 32 parameters and builds a ~900-byte instruction-level object:

  • 13 sub-object allocations via vtable allocator (vtable+24)
  • 4 linked-list structures for instruction chaining
  • 2 string buffers for instruction name and alternate name (via strlen + memcpy)
  • Architecture descriptor via sub_B19110(arch_id) at offset +408
  • Hash table using FNV-1a (seed 0x811C9DC5, prime 16777619) for instruction record lookup

The instruction unlink-and-recycle functions (sub_10ADF90, sub_10AE190) remove an instruction node from a doubly-linked list (head/tail at a1+48/56), update the count at a1+64, free operand attachments via vtable call, and return the node to a free-list at a1+72. The maximum instruction count per list is 16,383 (checked by sub_10AE7C0).

Encoding Pipeline Layers

The full encoding pipeline operates in three layers, from high-level IR to binary output:

Layer 1: High-level encode (0x1134160--0x114F380, ~132 functions) Populates full IR records before low-level packing. Uses sub_9B3C20(a1, a2, slot, type, mode, width, reg_id) for register operands and sub_9B3D60 for immediates. Handles 255->1023 sentinel translation for "don't care" register values. Sets opcode/modifier fields via sub_AFA910/sub_AFA930. Applies conditional fixups: e.g., if opcode==2038 && subopcode==2257, sets operand_slot+84 = 5.

Layer 2: Binary encoders (0x10EE900--0x1134160, ~400 functions) Reads operand fields from IR via sub_10BDxxx extractors, transforms through sub_10Bxxx lookup tables, and packs results into the 128-bit output word at *(QWORD*)(a1+40):

// Typical pattern (sub_10F91D0):
int v6 = sub_10BF170(operand_addr);       // extract register class
int v7 = sub_10B6180(lookup_table, v6);    // translate to encoding value
*(uint64_t*)(a1 + 40) |= ((uint64_t)v7 << 15);  // pack at bit position 15

Includes a register pair encoder (sub_112CDA0, 8.9 KB) that maps 40 register pair combinations (R0/R1, R2/R3, ... R78/R79) to packed output values at 0x2000000 intervals.

Layer 3: Template encoder stubs (0xD27000--0xEB2AE0, ~1,086 functions) The lowest-level stubs that directly write the encoding buffer via sub_7B9B80. These are the functions described by the encoder template above.

Variant/Sub-opcode Distribution

The variant field (bits[17:24], 8 bits) has a distribution that peaks at variant 0x05 with 128 functions, suggesting this is the default or most common variant (possibly .F32 type or the unmodified form):

VariantCountVariantCount
0x00210x0813
0x01250x0914
0x02620x0A10
0x03240x0B19
0x04200x0C14
0x051280x0D9
0x06300x0E11
0x07100x0F--0x2Fdecreasing

Maximum observed variant value is 0x2F (47), giving up to 48 sub-operations per major opcode.

SASS Emission Backend

The final stage of the encoding pipeline operates at the instruction-word level: 11 per-instruction-form bitfield packers at addresses 0x1B79940--0x1B9C220 take a pre-decoded instruction descriptor and pack all fields into a 128-bit SASS instruction word. These functions sit at Level 2 of a 4-level emission hierarchy:

Level 0: SM-target dispatch    (0xC4DF70, 0xC53330, 0xC54090, 0xC59610, 0xC5ABE0, 0xC5B5C0)
Level 1: Emission orchestrators (Zone C: 0x1BA0000-0x1BE5000, ~150 functions)
Level 2: Per-form bit packers   (Zone B: 0x1B79940-0x1B9C220, 11 functions, THIS SECTION)
Level 3: Register class encoders (Zone A: 0x1B4C000-0x1B76000, ~40 functions)

Each function has exactly 1 caller and 0 callees (pure bitfield packing, no external calls). Sizes range from 6836 to 6980 bytes of compiled code. All 11 share an identical combinator body (verified: same 453 LABEL_xxx targets, same 75 unique OR-mask constants, same max comparison value of 27). They differ only in two things: the opcode base constant, and the prologue field-packing sequence.

Input / Output Interface

int *__fastcall emit_instruction_form_X(int *a1) {
    // a1 = pre-decoded instruction descriptor (array of 32-bit ints)
    // Returns: pointer to output buffer (also accessible at *((_QWORD*)a1 + 14))
    int *result = *((_QWORD *)a1 + 14);  // output = 128-bit instruction word
    // result[0] = instruction bits [31:0]   (opcode base, guard pred, sched group)
    // result[1] = instruction bits [63:32]  (register operand fields, modifiers)
    // result[2] = instruction bits [95:64]  (immediate/offset, auxiliary fields)
    // result[3] = instruction bits [127:96] (predicate control, combinator encoding)
}

The input struct a1 is a flat array of pre-extracted instruction fields. Fields a1[0] through a1[3] carry common header values; a1[4] through a1[15] carry instruction-specific operand data (which indices are used depends on the instruction form).

Phase 1: Prologue -- Opcode Base and Field Packing

Every function begins with the same template, parameterized by different constants:

// 1. Load output buffer pointer
result = *((_QWORD *)a1 + 14);

// 2. OR opcode base into result[0] -- unique 12-bit constant per function
*result |= OPCODE_BASE;  // e.g., 0xA1E, 0x81B, 0x803

// 3. Pack guard predicate: bits [14:12] of result[0]
*result |= ((unsigned short)a1[1] << 12) & 0x7000;

// 4. Pack scheduling group: bits [16:15] of result[0]
*result |= (unsigned short)((unsigned short)a1[2] << 15);

// 5. Pack predicate encoding: bits [25:20] of result[3]
result[3] |= (a1[3] << 20) & 0x3F00000;

// 6. Pack instruction-specific operand fields (VARIES PER FUNCTION)
//    Each function packs a different set of a1[6..15] fields into
//    result[0], result[1], result[2] using different shifts and masks.

// 7. Set base combinator mask: bits [17:14] of result[3] = 0x3F
result[3] |= 0xFC000;

The prologue is the sole source of variation between functions. The field-packing differs in which a1[] indices are used, which shift amounts are applied, and which result[] DWORDs are targeted.

The 11 Functions and Their Opcode Bases

FunctionSizeOpcode BaseFamilyCaller Chain
sub_1B799406,900 B0xA1B0xAxxsub_1BA5340 via sub_C4DF70
sub_1B7B4406,868 B0x81B0x8xxsub_1BA5340 via sub_C4DF70
sub_1B877406,852 B0x2380x2xxsub_1BA8D80 via sub_C53330
sub_1B893506,836 B0x2130x2xxsub_1BA8E80 via sub_C54090
sub_1B8FFE06,852 B0x2020x2xxsub_1BA8D80 via sub_C53330
sub_1B925906,868 B0x8030x8xxsub_1BACB10 (direct)
sub_1B943906,964 B0x21D0x2xxsub_1BACDC0 via sub_C59610
sub_1B95ED06,980 B0xA1E0xAxxsub_1BACDC0 via sub_C59610
sub_1B985B06,852 B0x8040x8xxsub_1BAD6D0 (direct)
sub_1B9A4306,884 B0x8070x8xxsub_1BB1110 via sub_C5ABE0
sub_1B9C2206,884 B0x81A0x8xxsub_1BAD920 via sub_C5B5C0

The opcode bases cluster into three families by high nibble:

  • 0x2xx (4 functions): bases 0x202, 0x213, 0x21D, 0x238
  • 0x8xx (5 functions): bases 0x803, 0x804, 0x807, 0x81A, 0x81B
  • 0xAxx (2 functions): bases 0xA1B, 0xA1E

Phase 2: Combinator -- 3-Axis Predicate Encoding into result[3]

After the prologue, all 11 functions execute an identical ~1900-line decision tree. This combinator reads three integer values from the input struct and produces a single 32-bit mask that is ORed into result[3].

The three axes are:

  • axis0 = a1[0]: instruction class selector, values 0..5 (6 values)
  • axis1 = a1[4]: slot/form index, values 1..27 (26 populated, gap at 16)
  • axis2 = a1[N]: sub-mode flag, values 0 or 1 (N varies per function -- a1[8], a1[9], a1[10], a1[11], or a1[15])

The combinator exits immediately if all three axes are zero (!(axis0 | axis1 | axis2)). Otherwise it walks a nested decision tree that tests axis0 values (0 through 5), axis1 values (1 through 27), and axis2 values (0 or 1), and ORs the appropriate mask into result[3]:

// Reconstructed combinator logic (pseudocode):
if (axis0 == 0 && axis1 == 0 && axis2 == 0) return;

// For axis0 values 1-5 combined with axis1 values 1-15:
// result[3] |= prefix_for_axis0 | 0xFC000 | (axis1 << 9)
//
// For axis1 values 17-27 combined with axis2:
// result[3] |= base_mask_for_axis1  (if axis2 == 0)
// result[3] |= extended_mask_for_axis1  (if axis2 == 1)

Combinator Mask Encoding

The 75 unique masks in the FC/FD series decompose as:

result[3] bit layout for combinator-generated fields:
  bits [17:14] = 0x3F  (always set by prologue base 0xFC000)
  bits [13:9]  = slot_index  (5-bit, derived from axis1, values 1-27)
  bits [28:26] = axis0 prefix encoding (3-bit, for axis0 values 1-5)

The 5 prefix values correspond to axis0 encodings 1-5:

axis0 ValuePrefix OR'dPrefix Bits [28:26]
00x00000000 (no prefix)
10x40xxxxx001
20x80xxxxx010
30xC0xxxxx011
40x100xxxxx100
50x140xxxxx101

Combined with 15 slot values (axis1 = 1..15), this produces 5 x 15 = 75 masks in the 0xFC200--0x140FCE00 range.

For axis1 values 17--27, the masks shift into the 0xFE200--0xFF600 range. These slots use only the "no prefix" and "prefix 0x100" variants (axis0 values 0 and 4), and the axis2 flag selects between the two. This gives an additional 12 unique masks for the high-slot range (11 base + 11 extended, minus shared ones, equals 12 unique).

Why the Combinator Exists

The combinator encodes an architecture-independent mapping from a 3-dimensional instruction property coordinate to a hardware-specific bitfield pattern in the predicate/control section of the 128-bit instruction word. This section (bits [127:96]) controls:

  • Guard predicate assignment (bits [25:20] from prologue)
  • Scheduling mode (bits [17:14] base + combinator overlay)
  • Instruction form variant (bits [13:9] from combinator)
  • Predicate class / condition code routing (bits [28:26] from combinator)

The identical combinator across all 11 functions confirms that this is not an opcode-specific encoding but rather a cross-cutting encoding for predicate/scheduling state that applies uniformly to all instruction forms.

Equivalent Lookup Table

The entire 2000-line decision tree can be replaced by a flat table of 6 x 28 x 3 = 504 entries:

// Equivalent reconstruction:
static const uint32_t combinator_table[6][28][3] = { ... };
// Access: result[3] |= combinator_table[axis0][axis1][axis2];
// Table size: 504 * 4 = 2,016 bytes (vs ~6,800 bytes of code per function)

The compiler chose a decision tree over a table lookup, likely because the C++ source used nested switch/case statements (or if/else chains with early return), and the optimizer did not convert this to a table at -O2.

Zone B Function Map (Emission Cluster)

AddressSizeOpcode BaseCallerConfidence
sub_1B799406,900 B0xA1Bsub_1BA5340HIGH
sub_1B7B4406,868 B0x81Bsub_1BA5340HIGH
sub_1B877406,852 B0x238sub_1BA8D80HIGH
sub_1B893506,836 B0x213sub_1BA8E80HIGH
sub_1B8FFE06,852 B0x202sub_1BA8D80HIGH
sub_1B925906,868 B0x803sub_1BACB10HIGH
sub_1B943906,964 B0x21Dsub_1BACDC0HIGH
sub_1B95ED06,980 B0xA1Esub_1BACDC0HIGH
sub_1B985B06,852 B0x804sub_1BAD6D0HIGH
sub_1B9A4306,884 B0x807sub_1BB1110HIGH
sub_1B9C2206,884 B0x81Asub_1BAD920HIGH

SM89/90 Codec Layer

SM89 (Ada Lovelace) and SM90 (Hopper) share a pre-encoding instruction reordering layer absent from SM100 (Blackwell). This layer sits above the three-layer encoding pipeline: it manipulates Mercury IR instruction lists to optimize instruction interleaving before the encoding stubs pack bitfields. The entire cluster spans addresses 0x1226E80--0x1233D70, roughly 261 KB of compiled code across 18 functions.

Call Chain

sub_C60910 / sub_C5FEF0   SM-target dispatch (Level 0, 0xC5xxxx range)
  |
  v
sub_1233D70 (6 KB)         Orchestrator: guards on knob 487 and O-level > 1,
  |                        sets up cost-function parameters, calls A then B
  |
  +-> sub_122AD60 (112 KB)  Pass A: classify instructions + reorder within blocks
  +-> sub_122F650 (105 KB)  Pass B: scheduling-aware emission ordering across blocks
  +-> sub_A112C0            Post-pass finalization

The orchestrator sub_1233D70 is called only when optimization level exceeds 2 (sub_7DDB50(ctx) > 1). It reads floating-point cost weights from the target descriptor via knob offsets +7200, +7560, +7128, +7272 and passes them through to both passes. Default base weights are 1.8, -0.8, 3.2, -2.2.

Pass A: Instruction Classification and Reordering (sub_122AD60)

4,118 decompiled lines. Traverses every instruction in each basic block and sorts them into 4 linked-list queues by instruction category:

CategoryReturn CodeInstruction TypeQueue Role
Branch / control-flow0type 9 (BRA, EXIT, RET, ...)Held at block boundaries
Load1type 12 (LDG, LDS, ...)Scheduled early for latency hiding
Store2type 5 (STG, STS, ...)Deferred to maximize distance from load
General ALU4type 4 (IADD, FFMA, ...)Interleaved between memory ops
Uncategorized3other / missing infoTreated as general

The classifier is sub_1228670 (30 lines), which reads the instruction scheduling class via sub_7E2FE0 and returns 0--4. A companion predicate sub_1228EF0 (38 lines) returns 0 for types 9, 5, and 12 (the "special" categories), 1 for everything else.

After classification, Pass A performs register-class-aware instruction motion: it uses sub_91BF30 (register class builder), sub_91E390 (class query), and sub_91E610 (class intersection) to verify that moving an instruction does not violate register-class constraints. Instructions that pass the check have their operand flags updated at +48 (bit 0x40 = "moved" marker) and +96 (copy-chain tracking).

The reordering step sub_122AA30 (186 lines) performs the final within-block interleaving. sub_1227D90 (522 lines) handles the actual linked-list surgery: unlink an instruction from its current position and reinsert it at a new location.

Pass B: Scheduling-Aware Emission Ordering (sub_122F650)

3,917 decompiled lines. Takes the classified instruction lists from Pass A and determines the emission order that optimizes scheduling. Operates on 8 bitvector arrays allocated via the sub_BDxxxx bitvector library:

BitvectorPurpose
v521Main liveness set (all instructions)
v523Load-group register liveness
v525Store-group register liveness
v527ALU-group register liveness
v529Control-flow register liveness
v531Cross-block interference set
v533Scheduling priority set
v535Secondary interference set

Each bitvector is sized to the function's total register count (*(ctx+224)). Pass B iterates through instructions, populates the bitvectors with defined-register information via sub_BDBC70 (set bit), then merges category-specific vectors into the main set via sub_BDC5F0 (union) in an order determined by the dependency analysis.

The single switch at line 2578 dispatches on the instruction category:

case 4 (ALU):     merge load + store + ALU vectors into main
case 3 (branch):  merge load vector only
case 0 (uncategorized): merge store vector only
case 2 (load):    merge ALU vector only
case 1 (store):   no merge (control-flow kept separate)

Knob-derived flags control reordering aggressiveness:

  • Knob at target offset +7416 (index ~103): enable load reordering
  • Knob at target offset +7488: enable general reordering
  • All reordering disabled when *(ctx+1584)+372 == 12288 (specific regalloc config)

Pass B also maintains a red-black tree structure for the emission schedule, with standard left/right/parent pointers at node offsets 0, 8, 16.

Differences from SM100

AspectSM89/90SM100 (Blackwell)
Pre-encode reorderingPresent (sub_122AD60 + sub_122F650)Absent -- scheduling integrated into own pass
Instruction classification5-category scheme (branch/load/store/ALU/other)370-category opcode dispatch via megafunctions
Cost modelFloating-point heuristic (4 tunable weights)Table-driven via hardware profile records
Liveness tracking8 bitvectors per blockHandled in scheduling pass, not in encoding
Knob controlKnobs 103, 106, 218, 230, 487, 501Different knob set for Blackwell scheduler
Register class validationsub_91BF30/sub_91E390 per-move checkPer-instruction class check at encoding time
Binary encoder callsNone -- IR-level manipulation onlysub_7B9B80 (18,347 callers)

The SM89/90 pair operates entirely at the Mercury IR level and produces no packed instruction bits. It rewrites the instruction linked lists in each basic block to optimize scheduling, after which the standard encoding pipeline (Layers 1--3) runs on the reordered sequence. SM100 Blackwell does not need this layer because its scheduling infrastructure (documented in scheduling/algorithm.md) already integrates instruction ordering into the scheduling pass itself.

SM89/90 Codec Function Map

AddressSizeLinesIdentityConfidence
sub_1233D706 KB321sm89_orchestrator -- guards, cost params, calls A+BHIGH
sub_122AD60112 KB4,118sm89_classify_reorder -- instruction classification + block reorderingHIGH
sub_122F650105 KB3,917sm89_emission_order -- scheduling-aware emission orderingHIGH
sub_122AA30~3 KB186local_reorder -- within-block instruction interleavingHIGH
sub_1227D90~9 KB522instruction_reinsert -- unlink + reinsert at new positionHIGH
sub_122F1E0~6 KB330scheduling_heuristic -- cost-function comparison for emission orderMEDIUM
sub_1228670~0.5 KB30instruction_classify -- 5-category classifier (returns 0--4)CERTAIN
sub_1228EF0~0.5 KB38is_special -- predicate: types 9/5/12 return falseCERTAIN
sub_1226E80~0.3 KB22list_prepend -- insert instruction at list headCERTAIN
sub_1226EB0~5 KB274instruction_finalize -- post-reorder operand fixupHIGH
sub_1227820~1 KB77operand_offset_update -- adjust operand offsets after moveHIGH
sub_1227B60~0.5 KB31motion_check -- can instruction move to new position?HIGH
sub_1228FA0~2 KB100regclass_propagate -- propagate register class after moveHIGH
sub_12292B0~0.5 KB38queue_init_A -- initialize classification queueHIGH
sub_1229330~0.5 KB38queue_init_B -- initialize classification queueHIGH
sub_1229BD0~2 KB107tree_rebalance -- red-black tree rebalanceMEDIUM
sub_122A050~1 KB77pre_pass_init -- initialize pass A state objectHIGH
sub_122A1A0~2 KB139block_resize -- resize bitvector for new block countHIGH

Function Map

AddressSizeCallersIdentityConfidence
sub_7B9B80216 B18,347bitfield_insert -- core packer into 1280-bit bufferCERTAIN
sub_7B9D3038 B2,408clear_cbuf_slots -- memset(a1+468, 0xFF, 64)HIGH
sub_7B9D60408 B2,408encode_reuse_predicate -- reuse flags + guard predicateHIGH
sub_7BC030814 B6,147encode_register -- GPR operand encoderHIGH
sub_7BC360~500 B126encode_uniform_register -- UR operand encoderHIGH
sub_7BC5C0416 B1,449encode_predicate -- predicate operand encoderHIGH
sub_7BCF00856 B1,657encode_immediate -- immediate/cbuf operand encoderHIGH
sub_7BD260~300 B96decode_finalize -- extract control bitsHIGH
sub_7BD3C0~500 B286decode_register -- GPR operand decoderHIGH
sub_7BD650~400 B115decode_register_alt -- destination register decoderHIGH
sub_7BE090~400 B50decode_predicate -- predicate operand decoderHIGH
sub_10B618021 B8,091encode_bool_field -- 1-bit opcode-to-control mappingHIGH
sub_10B616021 B2,205encode_bool_field_B -- 1-bit flag variantHIGH
sub_10B614021 B1,645encode_bool_field_C -- 1-bit flag variantHIGH
sub_10AFF8011 KB3instruction_constructor -- 32-param object builderHIGH
sub_10ADF902.2 KB357instruction_unlink -- linked-list remove + recycleHIGH
sub_10B0BE06.5 KB--hash_table_insert_64 -- FNV-1a, 8-byte key, 4x resizeHIGH
sub_10B1C303.9 KB--hash_table_insert_32 -- FNV-1a, 4-byte keyHIGH
sub_10C0B20180 KB3,109setField -- field value writer dispatchHIGH
sub_10D5E60197 KB961getFieldOffset -- field bit-position lookup dispatchHIGH
sub_10E32E0187 KB72hasField -- field existence query dispatchHIGH
sub_10CCD80142 KB4setFieldDefault -- default value writer dispatchMEDIUM
sub_10CAD7068 KB74getOperandFieldOffset -- per-operand field offset dispatchHIGH
sub_10C769065 KB288setOperandField -- per-operand field writer dispatchHIGH
sub_AF7DF0--7,355encoded_to_ir_register -- hardware reg to IR translationHIGH
sub_AF7200--552encoded_to_ir_predicate -- hardware pred to IR translationHIGH
sub_EB30401.9 KB--decode_dispatcher -- binary search on instruction typeHIGH
sub_112CDA08.9 KB--register_pair_encoder -- 40-pair mapping via if-chainHIGH

Cross-References