Triton: The Compiler That Pretends to Be a Library Triton is a compiler with a Python frontend that parses a function's AST, runs it through an MLIR pipeline, and emits a GPU binary, never executing the Python function as Python. The compiler handles thread-to-data mapping, shared memory, and tensor core instructions automatically, but users cannot reach past the abstraction when it gets in the way. This matters because Triton sets what it can and cannot do, tiling GEMM, staging data through shared memory, mapping blocks to warps, and picking tensor core instructions without the user writing PTX or managing thread indices. Triton: The Compiler That Pretends to Be a Library Triton is a compiler with a Python frontend. The @triton.jit decorator does not decorate a function. It parses the function’s AST, runs it through an MLIR pipeline, and emits a GPU binary. The Python function never runs as Python. This matters because it sets what Triton can and cannot do. It tiles a GEMM for you, stages data through shared memory, maps blocks to warps, and picks tensor core instructions. You never write a line of PTX or manage a thread index. But you also cannot reach past the abstraction when it gets in the way. This post walks through how Triton compiles code, what the compiler decides on your behalf, where the abstraction falls short, and where Triton fits in the ML systems stack. The Programming Model: Blocks, Not Threads In CUDA you write code for one thread and the hardware runs it across thousands. Triton flips this. You write code for a block of data and the compiler splits it across threads. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 python import triton import triton.language as tl @triton.jit def softmax kernel input ptr, output ptr, n cols, BLOCK SIZE: tl.constexpr, : 1. Each "program" handles one row. No thread indexing. row idx = tl.program id 0 col offsets = tl.arange 0, BLOCK SIZE mask = col offsets < n cols 2. Load an entire row as a block. The compiler picks coalescing, vector width, and predication. row = tl.load input ptr + row idx n cols + col offsets, mask=mask, other=-float 'inf' 3. Block-level reductions. The compiler turns these into warp shuffles and shared memory reductions. row max = tl.max row, axis=0 numerator = tl.exp row - row max denominator = tl.sum numerator, axis=0 result = numerator / denominator tl.store output ptr + row idx n cols + col offsets, result, mask=mask The key operations: : Tile-level memory access. The compiler turns these into coalesced global memory instructions with predication from the tl.load / tl.store mask argument.: Block-level matrix multiply. The compiler emits tl.dot mma.sync Ampere , wgmma Hopper , or tcgen05.mma Blackwell depending on the target.: Block-level reductions. The compiler lowers these to warp shuffles tl.max , tl.sum SHFL.BFLY or shared memory reductions depending on block size.: Compile-time constants. tl.constexpr BLOCK SIZE gets baked into the binary. Different values produce different kernels — hence the autotuning step. You never write threadIdx.x . There is no syncthreads . There is no shared memory declaration. The compiler handles all of it. That is both the selling point and the ceiling. The Compilation Pipeline Triton lowers code through four IRs: 1 fn:1 php graph LR PY "Python AST" -- TTIR "Triton IR