Automatic barriers
Wherever workgroup memory written by one thread is read by another, a barrier is inserted in uniform code before the read.
Bento compiles to its own IR and lowers that directly to what your graphics API requires. The whole infrastructure is one C library without dependencies, small enough to vendor, and safe to run on untrusted input.
Fragment shader with a struct of inputs and a struct of outputs. Each field is bound through an attribute.
import std;
struct Pixel {
[[location(0)]] uv: f32x2,
}
struct Color {
[[color(0)]] rgba: f32x4,
}
[[stage(.Fragment)]]
fn main(px: Pixel) -> Color {
let corner = px.uv.x * px.uv.y;
let glow = std::smooth_step(0.0, 1.0, corner);
return { f32x4 { px.uv, glow, 1.0 } };
}
bento-c hello.bento bento-llc hello.bbc bento-llc --target=spirv hello.bbc
Barriers, subgroups, and uniformity are concepts built into the language rather than thinly typed intrinsics bolted onto C. Language tour
How widely every value is shared is proven during compilation for every rung from the whole dispatch down to one thread. Derivatives, simdgroup operations, and bindless indices are checked against it.
Wherever workgroup memory written by one thread is read by another, a barrier is inserted in uniform code before the read.
Built-in values such as the thread id, and values defined in the shader, are available at any depth of the call chain through checked with clauses. There are no globals and no parameters passed by hand.
Bracket parameters with built-in bounds and structural interfaces. Generics are type checked once and can be exported through bytecode.
Under the type checker, code is selected with const if and unrolled with const for. Modules and a linker are used in place of #include.
Every reference type includes the memory space: &thread, &workgroup, &storage, or &constant. Buffers and workgroup memory can be passed to functions on every target.
An IR and a set of language agnostic tools that work on it. The Bento language and compiler are a small client on top of all that, kind of like how clang is the C/C++ frontend for LLVM. Tools, IR and bytecode, Library
Blocks belong to if and loop instructions rather than to a control flow graph. IR can be built, edited, printed, and verified from other programs.
Modules can be linked at build time or at run time, left incomplete, and mixed with IR from any source.
Compiler, linker, optimizer, code generator, disassembler, validator, stripper, and language server.
Inlining, mem2reg, scalar replacement of aggregates, folding, if-conversion, CSE, dead store and redundant load elimination, aggressive dead code elimination, and load narrowing. Comprehensive rule-driven algebraic simplifications.
One C header. Memory, files, and diagnostics are provided by the host through callbacks.
No source or bytecode can crash the library, hang it, or cause out of memory without being reported and gracefully handled. If you can provide an input that proves otherwise, it's worth up to $500 USD as part of our bug bounty program.
| Bento | GLSL, HLSL, WGSL | |
|---|---|---|
| Output | Lowered straight to SPIR-V, AIR, or GLSL | glslang, DXC, Tint, or a transpiler in between |
| Reuse | Modules, imports, and a linker | #include |
| Variants | Type checked const if, const for, and generics | Preprocessor macros |
| Uniformity | Proven for every value and checked across modules | Unchecked in GLSL and HLSL but checked for derivatives and barriers in WGSL |
| Barriers | Inserted automatically | Written by hand |
| Built-ins | Context parameters checked per stage | Globals or semantics |
| Buffers in functions | References with a memory space | Not allowed in GLSL |
| Results | Multiple return values | out parameters |
| Embedding | One C library without dependencies | Large C++ code bases |
| Access | Every stage public, down to the IR | Source in, code out |