B+ v4.6.4-beta — Compiled .plan / .metal Language (Frontend → HIR → BIR → MIR → Targets)
B+ компилирует .plan / .metal файлы напрямую в машинный код x64 и упаковывает в Windows PE (.exe/.dll). Никаких ассемблеров, линкеров, LLVM — весь кодогенератор и оптимизатор написаны с нуля на Zig.
Что нового в v4.6.4-beta
- Новая архитектура: Frontend → HIR → BIR → MIR → Targets
- Разделение слоёв: frontend / middle(BIR) / backend(MIR) / targets
- Новый BIR Core: типизированные Module, Function, Block, Value, Instruction, TypeSystem
- BIR Optimizer и Analysis: PassManager, CFG, AnalysisManager
- Новый MIR Core: MFunction, MBlock, MInst, Operand, Opcode, Phi
- Target Backend: common + x64 (ISel, encoder, frame manager, regalloc)
- Рефакторинг MIR: CMP/SETCC, 4-operand IDiv, новый Ret, CondCode для FCmp
- Исправлены critical edge, Phi lowering и проблемы vreg
- Полный pipeline: BIR → MIR → x64 → executable
- Парсер теперь syntax-only: убран Dialect из парсера, валидация домена перенесена на этап HIR
- AST реорганизован:
ProgramNode { common, plan, metal }— общие конструкции, план и метал отдельно - Единый файл
.b+для обоих доменов (ранее.plan/.metalотдельно) - Слой Frontend: парсер не знает о доменах, принимает весь синтаксис, валидация на HIR
- Слой Middle (BIR): верификатор разбит на 7 модулей (verify/cfg, verify/ssa, verify/phi, verify/function, verify/types, verify/memory, verify/instructions), CFG строится из терминаторов на лету
- Слой Backend (MIR): CMP_FLAGS+SETCC, 4-operand IDiv, Ret как union, CondCode для FCmp
- Слой Targets: common + x64 (ISel, encoder, frame manager, regalloc)
- BIR Verifier: модульная система верификации из 7 модулей (CFG, SSA, phi, функции, типы, память, инструкции)
- Структурированная диагностика: коды ошибок, контекст инструкций, список ошибок для интеграции с тулами
- CFG bounds checking: некорректные адреса переходов ловятся вместо panic
- SSA verification: тесты на use-before-def и нарушение доминантности
- Исправлены E2E тесты верификатора: правильный порядок инструкций (терминатор последний), phi incoming через alloc.dupe
- Исправлен buildCFG: проверка границ перед доступом к блокам по индексу
- HIR Unification: удалён
middle/hir/(дублирующий tree-based HIR).StateItem,KernelItem,HirAttr,DispatchSizeперенесены в единыйfrontend/hir/item.zig. Pipeline:AST → Unified HIR → TIR → BIR → MIR → x64 - THIR (Typed HIR) — новый слой между HIR и BIR. Статически типизированная IR:
ValueDef(ty, storage, expr),ThirExpr(18 вариантов),ThirStmt(9 вариантов),Terminator(6 вариантов),CastKind(16 вариантов). Pipeline расширен:AST → Unified HIR → THIR → BIR → MIR → x64 - HIR → THIR lowering:
LowerContextс привязкойValueId → ExprId,classifyCast()для определения kind кастов, трансляция state/event declarations - THIR → BIR lowering: модель
ValueBinding(ssa/stack_slot/address) для корректного SSA. IndirectionValueId → ExprId → Exprвместо прямого индекса.emitPhi,param_map, проекции Place с отслеживанием типов - Memory/ownership: исправлен контракт владения
MFunction— backend мутирует переданные mfuncs, защита от shallow-copy deinit и use-after-free приemitModule - Тесты:
test-thir,test-thir-to-bir,test-backend,test-cpu,test-fuzz; новыйsrc/compiler/test_exports.zig— единый экспорт compiler modules, исправлены Zig module boundaries - Backend validation: MIR verification, x64 IR verification, register/operand/instruction validation
bpc check: прогон программы через все верифицируемые слои (Parser → HIR → THIR → BIR → MIR → x64) без кодогенерации;PASS/FAILпо слоямbpc doctor: health-check компилятора — рантайм, линкер, полный верифицируемый pipeline на встроенной программе, итогCompiler stability: OK
Что такое B+
B+ — это язык программирования, который превращает написанный код прямо в готовую программу для Windows (.exe или .dll). В отличие от многих языков, B+ не использует внешние компиляторы или линкеры — весь процесс сборки выполняет собственный компилятор.
Код B+ хранится в файлах с расширением: example.b+
В B+ есть два основных режима программирования:
PLAN — описание логики состояний
PLAN нужен, когда программа должна работать как набор состояний и переключаться между ними по событиям.
Например:
- меню игры
- состояния персонажа
- игровые режимы
- сетевые протоколы
- обработчики событий
state Locked {
on open [key == 1] -> Opened
entry {
print("locked\n")
}
}
state Opened {
on close -> Locked
entry {
print("opened\n")
}
}
Что здесь происходит:
- Программа начинает в состоянии
Locked - Если приходит событие
openи есть ключ (key == 1) — переходит вOpened - При входе в состояние выполняется код внутри
entry
METAL — обычное программирование
METAL предназначен для создания обычного кода:
- функций
- алгоритмов
- вычислений
- работы с памятью
- низкоуровневых систем
fn fibonacci(n: i64) -> i64 {
if n <= 1 {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
Этот код создаёт функцию вычисления чисел Фибоначчи.
Один язык — два подхода
PLAN и METAL используют один синтаксис B+, но предназначены для разных задач:
| Режим | Назначение |
|---|---|
| PLAN | логика состояний и событий |
| METAL | алгоритмы и системный код |
Компилятор сам определяет, к какому режиму относится код.
B+ объединяет простоту языков высокого уровня с контролем системного программирования, позволяя создавать как игровую логику, так и низкоуровневые программы.
Самый простой способ проверить, что компилятор работает:
- Создайте или возьмите любой файл B+, например:
hello.b+
-
Перетащите файл
hello.b+мышкой прямо наbpc.bat. -
Компилятор автоматически:
- скомпилирует программу;
- создаст рядом файл
hello.exe; - сразу запустит его.
Если после перетаскивания появился hello.exe и программа выполнилась — значит компилятор установлен и работает правильно.
Содержание
- Быстрый старт
- Команды компилятора
- Синтаксис языка (.b+)
- Синтаксис METAL
- Типы данных
- Примеры
- Оптимизатор BIR — бенчмарки и архитектура
- Сборка из исходников
- Структура проекта
- Лицензия
- Контакты
1. Быстрый старт
Перетащи файл .b+ на bpc.bat — скомпилирует в .exe и запустит.
Или через консоль:
zig\zig-out\bin\bpc.exe run hello.b+
Самый простой способ проверить, что компилятор работает
- Создайте файл
hello.b+в папкеC:\B-Plus:
state Hello {
entry { print("Hello World!\n") }
}
-
Перетащите файл
hello.b+мышкой прямо наbpc.bat. -
Компилятор автоматически:
- скомпилирует программу;
- создаст рядом файл
hello.exe; - сразу запустит его.
Если после перетаскивания появился hello.exe и программа выполнилась — значит компилятор установлен и работает правильно.
2. Команды компилятора
Синтаксис
bpc run <входной.b+> — скомпилировать и сразу запустить
bpc dll <входной.b+> — скомпилировать в DLL
bpc build <входной.b+> [-o <каталог>] — сгенерировать C++ UE5 плагин (6 файлов)
bpc hlsl <входной.b+> — сгенерировать HLSL шейдер
bpc gpu <входной.b+> — сгенерировать DXIL
bpc cpp <входной.b+> — сгенерировать C++ код
bpc mir <входной.b+> — сгенерировать COFF .obj
bpc bpl <входной.b+> — понизить B+ до BIR и вывести
bpc ir <входной.b+> — вывести BIR pipeline
bpc cfg <входной.b+> — вывести граф потока управления
bpc dom <входной.b+> — вывести дерево доминирования
bpc loops <входной.b+> — вывести иерархию циклов
bpc test <тест.bpt> — запустить тест
bpc dll <input.b+> [-o <output.dll>] [-exports <name1,name2,...>]
Компилирует .b+ файл в DLL с таблицей экспорта. Все export entry или перечисленные в -exports становятся экспортируемыми функциями.
| Шаг | Описание |
|---|---|
| 1 | Читает файл .b+ целиком в память |
| 2 | Разбирает (парсит) исходный код в AST |
| 3 | Генерирует машинный код x64 с DllMain (возвращает TRUE) |
| 4 | Создаёт таблицу импорта (kernel32.dll + runtime) |
| 5 | Строит таблицу экспорта (Export Directory Table, EAT, ENPT, EOT) с сортировкой ENPT по алфавиту (требование Windows для GetProcAddress) |
| 6 | Упаковывает всё в формат PE (DLL), секция .text — RWX (0xE0000020) |
| 7 | Записывает результат на диск |
Примеры:
bpc dll test.b+ -o test.dll -exports Init,Update
bpc dll module.b+
bpc build <input.b+> [-o <output_dir>]
Генерирует C++ UE5 плагин из B+ файла с описанием pipeline (6 файлов).
| Шаг | Описание |
|---|---|
| 1 | Читает файл .b+ целиком в память |
| 2 | Разбирает описание pipeline |
| 3 | Генерирует TSSShaders.h / TSSShaders.cpp |
| 4 | Генерирует TSSRuntime.h / TSSRuntime.cpp |
| 5 | Генерирует TSSViewExtension.h / TSSViewExtension.cpp |
| 6 | Записывает 6 файлов в выходной каталог |
Если -o не указан, файлы записываются в каталог исходного файла.
Примеры:
bpc build pipeline.b+ → 6 C++ файлов рядом с pipeline.b+
bpc build pipeline.b+ -o ./Plugin → 6 C++ файлов в ./Plugin
bpc gpu <input.b+> [-o <output>] / bpc hlsl <input.b+> [-o <output.hlsl>]
Генерирует HLSL-код из B+ файла с блочным kernel { ... } синтаксисом или старым (legacy @bind/@cbuffer). bpc hlsl автодетектит синтаксис.
| Шаг | Описание (новый pipeline) |
|---|---|
| 1 | Читает файл .b+ целиком в память |
| 2 | Разбирает (парсит) в GPU AST (gpu_ast.zig) |
| 3 | Семантический анализ (gpu_sema.zig): дубликаты регистров, лимиты, numthreads |
| 4 | Понижение до GPU IR (gpu_lower.zig): GPU AST → SSA IR |
| 5 | Генерация HLSL из IR (gpu_hlsl.zig) |
| 6 | Записывает результат на диск |
Аннотации:
// Текстуры
g_InputColor: @bind(t, 0, float4) // Texture2D<float4> : register(t0)
g_OutputColor: @bind(u, 0, float4) // RWTexture2D<float4> : register(u0)
g_OutputUAV: @bind(u, 1, uint, globallycoherent) // globallycoherent RWTexture2D<uint>
// Сэмплер
linearClamp: @bind(s, 0) // SamplerState : register(s0)
// Константный буфер
inputSize: @cbuffer(FSR2Constants, 0, float2) // cbuffer FSR2Constants : register(b0) { float2 inputSize; ... }
// Shared memory
sharedMem: @groupshared(sharedMem, 256) // groupshared float sharedMem[256];
В теле entry цикл for(x, y, w, h) транслируется в uint x = tid.x; if (x >= w) return;. HLSL-интринсики (WaveActiveSum, InterlockedAdd, mad, lerp и др.) проходят насквозь.
Пример:
bpc hlsl fsr2_easu.b+ -o fsr2_easu.hlsl
dxc -T cs_6_6 -E main -Fo fsr2_easu.cso fsr2_easu.hlsl
bpc run <input.b+>
Что делает:
| Шаг | Описание |
|---|---|
| 1 | Компилирует <input>.exe |
| 2 | Запускает полученный .exe |
| 3 | Перехватывает stdout и печатает в консоль |
| 4 | Возвращает код завершения программы |
Примеры:
bpc run traffic.b+ — компилирует и сразу запускает
bpc run hello.b+ — компилирует и сразу запускает
bpc check <input.b+>
Проверяет программу через все верифицируемые слои компилятора (Parser → HIR → THIR → BIR → MIR → x64) без кодогенерации. Выводит PASS для каждого слоя.
bpc check hello.b+ — проверить программу, не компилируя
Код возврата: 0 — все слои проверены, 1 — ошибка (указывается слой).
bpc doctor
Health-check компилятора: наличие встроенного рантайма (minrt.obj), наличие линкера (lld-link), прогон встроенной программы через полный верифицируемый pipeline.
bpc doctor — проверка здоровья компилятора
Коды возврата
| Код | Значение |
|---|---|
| 0 | Успех |
| 1 | Ошибка: неверные аргументы или файл не найден |
| >0 | Код завершения скомпилированной программы (при использовании run) |
Примечания
- Компилятор не использует внешние ассемблеры, линкеры или LLVM — весь машинный код генерируется самостоятельно.
- Команда
bpc runкомпилирует в.exeи сразу запускает. - Команда
bpc buildгенерирует C++ UE5 плагин (а не .exe).
3. Синтаксис языка
3.1 Состояния
state <Имя> {
...
}
Состояние — базовый строительный блок. Внутри могут быть переменные, переходы, entry/exit-блоки.
state Red {
on timer -> Green
entry { print("RED\n") }
}
3.2 Переходы (on)
on <событие> -> <ЦелевоеСостояние>
Когда приходит событие (строка из stdin), автомат переходит в указанное состояние.
state Green {
on timer -> Yellow
on pedestrian -> Red
}
3.3 Безусловные переходы (always)
always -> <ЦелевоеСостояние>
Переход происходит сразу при входе в состояние, без ожидания события.
state Init {
always -> Menu
}
3.4 Вход (entry)
state Door {
entry { print("entered\n") }
on open -> Opened
}
entry { ... } — выполняется при входе в состояние.
3.5 Переменные
var <имя>: <тип> [= <значение>]
Объявляются внутри состояния. Типы: int8, int16, int32, int64, u8, u16, u32, u64, byte, bool, short, int, float и т.д.
state Counter {
var count: int = 0
on tick -> Self {
count += 1
}
}
Можно объявлять несколько переменных через запятую:
var x: int, y: int, name: int
Допускается сокращённая запись — идентификатор без var и типа:
state S {
x // эквивалентно var x: i64
}
Такие голые идентификаторы автоматически регистрируются как переменные i64.
3.6 Присваивания
Внутри entry { }, exit { } или тела перехода:
var x: int
on event -> Next {
x = 42
x += 1
x -= 5
}
Поддерживаются операторы =, +=, `-=". В правой части можно использовать числа и имена переменных.
Запись через указатель — если левая часть это *<имя>, то значение записывается в память по адресу, хранящемуся в переменной:
*ctl = new_value // MOV [RCX], RAX — запись по указателю
result = *ctl // чтение: RAX = [RCX]
Используется для работы с TLS-сохранёнными указателями на persistent-кучу.
3.7 Печать (print)
print("строка")
Печатает строку в stdout. Поддерживаются escape-последовательности \n, \r, \t.
state Hello {
entry { print("Hello, world!\n") }
}
3.9 export entry
export entry <Имя> {
...
}
Экспортируемая точка входа — компилируется как функция, видимая извне DLL. Используется при сборке DLL (bpc dll) вместе с флагом -exports.
export entry TSS_Init {
print("init\n")
}
3.9 Entry point
entry <Имя> {
...
}
Точка входа — выполняется один раз при старте программы.
entry main {
print("Starting...\n")
}
3.11 Перечисления (enum)
enum <Имя> {
Член1,
Член2,
...
}
Глобальное объявление перечисления.
enum Color {
Red,
Yellow,
Green
}
3.11 Комментарии
// однострочный комментарий
-- тоже комментарий
4. Синтаксис METAL
METAL — это второй домен B+ (наряду с PLAN). Используется для функций, переменных, структур, указателей, if/while/for, составных присваиваний.
4.1 Типы
| Тип | Размер (байт) |
|---|---|
i8 / u8 / bool |
1 |
i16 / u16 |
2 |
i32 / u32 |
4 |
i64 / u64 |
8 |
int |
алиас i64 |
uint |
алиас u64 |
*T |
8 (указатель) |
void |
0 |
4.2 Функции
fn add(a: i64, b: i64) -> i64 {
a + b
}
fn main() {
print_i64(add(3, 4));
}
Последнее выражение — неявный return. Можно явно:
fn max(a: i64, b: i64) -> i64 {
if a > b {
return a;
}
return b;
}
4.3 Внешние функции
extern fn print_i64(x: i64);
extern fn read_i64() i64;
extern fn bplus_malloc(size: i64) i64;
extern fn bplus_free(ptr: i64);
extern fn bplus_exit(code: i64);
4.4 Переменные
var x: i64 = 42;
var y;
var z = 10;
x = 10;
4.5 Структуры
struct Point {
x: i64,
y: i64,
}
Можно в строку: struct Point { x: i64, y: i64 }
var p: Point;
p.x = 10;
p.y = 20;
print_i64(p.x);
Литералы:
var p = Point { x: 10, y: 20 };
var q = Point {
x: 30,
y: 40,
};
4.6 Указатели
var x: i64 = 42;
var p: *i64 = &x;
var y: i64 = *p;
*p = 10;
var addr: *i64 = &p.x;
4.7 If/else
if x > 5 {
print_i64(1);
} else {
print_i64(0);
}
if (x > 5) {
print_i64(1);
}
4.8 While
while i < 3 {
print_i64(i);
i += 1;
}
break / continue:
while i < 10 {
if i == 5 { break; }
if i == 2 { i += 1; continue; }
print_i64(i);
i += 1;
}
4.9 For
for i in 0..10 {
print_i64(i);
}
4.10 Составные присваивания
x += 1;
y -= 5;
z *= 2;
4.11 Операторы
| Оператор | Описание |
|---|---|
* / / |
умножение, деление |
+ / - |
сложение, вычитание |
== / != / > / < / >= / <= |
сравнения |
&& |
логическое И |
\|\| |
логическое ИЛИ |
4.12 Комментарии
// однострочный комментарий
4.13 Сообщения об ошибках
error[UnknownVariable]: test_error.b+:4:1
4 | print_i64(y);
| ^
4.14 CLI
bpc run <input.b+> [-o <output.exe>]
bpc mir <input.b+> [-o <output.obj>]
B+ Source (.b+)
│
┌───────────────┴───────────────┐
│ │
▼ ▼
PLAN Domain METAL Domain
│ │
└───────────────┬───────────────┘
▼
Parser
│
▼
AST
│
▼
HIR
│
▼
BIR (SSA)
│
mem2reg → CFG → SCCP → InstCombine
→ Constant Folding → GVN → LICM
→ Loop Unroll → DCE
│
▼
MIR
│
SSA Destroy → AddrFold → CopyProp
→ Peephole → DCE
│
▼
x64 Backend
│
┌────────────┴────────────┐
▼ ▼
PE (.exe / .dll) COFF (.obj)
5. Типы данных
| Тип | Размер (байт) |
|---|---|
int8, i8, u8, byte, bool |
1 |
int16, i16, u16, short, half |
2 |
int32, i32, u32, int, uint, float |
4 |
int64, i64, u64 (и всё остальное) |
8 |
6. Примеры
Светофор
state Green {
on timer -> Yellow
entry { print("GREEN\n") }
}
state Yellow {
on timer -> Red
entry { print("YELLOW\n") }
}
state Red {
on timer -> Green
entry { print("RED\n") }
}
Ввод: timer\n переключает состояния.
Счётчик
state Count {
var n: int = 0
on inc -> Self { n += 1 }
on show -> Show
}
state Show {
always -> Count
entry { print("n="); print("?\n") }
}
Охраняемый переход
state Door {
on open [key == 1] -> Opened
entry { print("locked\n") }
}
state Opened {
on close -> Door
entry { print("opened\n") }
}
7. Оптимизатор BIR — бенчмарки и архитектура
Архитектура backend
Backend компилятора построен по архитектуре, аналогичной LLVM:
BIR (SSA) ──── MIR (Machine IR) ──── x64 Machine Code
│ │ │
├─ mem2reg ├─ SSA destruction ├─ Linear Scan RA
├─ cfgsimplify ├─ addr_fold (LEA) ├─ Frame Manager
├─ SCCP ├─ copy propagation ├─ Instruction Encoder
├─ InstCombine ├─ DCE └─ COFF/PE
├─ ConstantFolding └─ peephole
├─ GVN
├─ LICM
├─ Unroll
└─ DCE
BIR Optimization Pipeline
| Проход | Описание |
|---|---|
| mem2reg | Продвижение памяти в SSA-регистры |
| cfgsimplify | Упрощение графа потока управления |
| SCCP | Распространение условных констант |
| InstCombine | Алгебраические тождества, свёртка сравнений |
| ConstantFolding | Вычисление константных выражений, max/min |
| GVN | Глобальная свёртка значений (CSE для commutative ops) |
| LICM | Вынос инвариантов из циклов |
| Unroll | Развёртка коротких циклов |
| DCE | Удаление мёртвого кода |
MIR Optimization Pipeline
| Проход | Описание |
|---|---|
| SSA Destruction | Замена phi на mov через CopyProp |
| AddrFold | Синтез LEA из адресной арифметики [base+index*scale+disp] |
| Copy Propagation | Распространение копий (3 итерации) |
| DCE | Удаление мёртвого кода |
| Peephole | Константная свёртка, оптимизация сравнений |
Бенчмарки оптимизаций
8. Сборка из исходников
Требуется Zig (master, >= 0.14).
cd zig
zig build
Или напрямую:
cd zig
zig build-exe src/main.zig -femit-bin=bpc.exe
После сборки:
bpc.exe run example.b+
9. Структура проекта
Архитектура компилятора
Frontend (парсер, AST, семантика)
│
▼
HIR (High-Level IR) — BIR SSA
│ mem2reg → cfgsimplify → SCCP → InstCombine → ConstantFolding
│ → GVN → Unroll → LICM → ForwardStoreToLoad → DeadStoreElimination → DCE
▼
MIR (Machine IR) — target-independent
│ SSA Destroy → AddrFold → CopyProp → Peephole ×3 → DCE
▼
Targets (code generation)
│ ISEL → RegAlloc → Encoding
▼
Object (PE/COFF → .exe)
10. Лицензия
MIT License
MIT License
Copyright (c) 2025 bylka2W
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
11. Контакты
- GitHub: github.com/bylka2W
- Репозиторий: github.com/bylka2W/B-Plus
- GitVerse: gitverse.ru/bylka2W/B-Plus
- GitFlic: gitflic.ru/project/bylka2w/b-plus
- GitLab: gitlab.com/bylka2W/b-plus
- Автор: bylka2W
B+ v4.6.4-beta — Compiled .b+ Language (Frontend → HIR → BIR → MIR → Targets)
B+ compiles .b+ files directly to x64 machine code and packages them into Windows PE executables (.exe/.dll). No assemblers, linkers, or LLVM — the entire code generator and optimizer are written from scratch in Zig.
What’s new in v4.6.4-beta
- Frontend → HIR → BIR → MIR → Targets architecture — full compiler migration to a multi-level architecture inspired by LLVM/rustc with strictly one-way dependency flow.
- frontend / middle / backend split — compiler source moved from flat
compiler/parser/andcompiler/backend/bir/intocompiler/frontend/,compiler/middle/bir/,compiler/backend/mir/,compiler/backend/targets/. - BIR core module — new
bir/core/with typedModule,Function,Block,Value,Instructionand aTypeSystem. - BIR optimizer framework —
bir/optimizer/withPassManagerandPassResultfor building optimization pipelines. - BIR analysis —
bir/analysis/withAnalysisManagerandcfg.zig(CFG construction). - MIR core module —
mir/core/with typedMFunction,MBlock,MInst,MOperand,MOpcode,PhiInst,PhiIncoming. - Targets abstraction —
targets/common/(shared target) andtargets/x64/with instruction selection (isel/), encoder, frame manager, register allocator. - Critical edge fix — fixed
testCriticalEdgePhihang: epilogue now emitted after each block ending withret, preventing infinite loop when critical edge splitting places blocks after a return block. - MIR instruction model refactoring — CMP_FLAGS + SETCC:
CmpInstno longer writes a destination (FLAGS-only); newSetCCInstmaterializes the comparison result into a vreg.IDivInstis now 4-operand (dividend, divisor, quotient, remainder).RetInstis a union (void/value).FCmpInstusesCondCodeenum instead of rawu8. - Safe defaults for unregistered vregs — all
orelse unreachablein x64 ISel replaced with safe defaults (.gpr/.i64), preventing panics when vreg registration is missing. - 9/9 BIR → MIR → x64 → execute E2E tests pass (was 7/8).
- BIR Verifier overhaul — modular verification system with 7 specialized verifier modules (CFG correctness, SSA form, phi nodes, function invariants, type checking, memory safety, instruction validity).
- HIR Unification: removed
middle/hir/(duplicate tree-based HIR).StateItem,KernelItem,HirAttr,DispatchSizemoved into unifiedfrontend/hir/item.zig. Pipeline:AST → Unified HIR → TIR → BIR → MIR → x64 - Structured diagnostics — error codes, per-instruction context, structured error lists for tooling integration.
- CFG bounds checking — out-of-range branch targets are now caught and reported instead of panicking.
- SSA verification — tests for use-before-def detection and dominance violation detection.
- Verifier E2E test fixes — correct instruction ordering (terminator must be last), phi incoming slices use
alloc.dupe, return values use phi. - buildCFG bounds check — CFG construction validates block indices before access.
- Parser is now syntax-only —
Dialectremoved from parser; domain validation moved to HIR lowering stage. - AST restructured —
ProgramNode { common, plan, metal }separating shared constructs from domain-specific ones. - Unified
.b+extension for both domains (previously separate.plan/.metal). - Frontend layer — parser is domain-agnostic, accepts all syntax, validation happens at HIR.
- Middle layer (BIR) — verifier split into 7 modules under
verify/directory (cfg, ssa, phi, function, types, memory, instructions); CFG built on-demand from terminators. - Backend layer (MIR) — CMP_FLAGS+SETCC, 4-operand IDiv, Ret as union, CondCode for FCmp.
- Targets layer — common + x64 (ISel, encoder, frame manager, regalloc).
What is B+
B+ is a programming language that turns written code directly into a ready Windows program (.exe or .dll). Unlike many languages, B+ doesn’t use external compilers or linkers — the entire build process is handled by its own compiler.
B+ code is stored in files with the extension: example.b+
B+ has two main programming modes:
PLAN — state logic description
PLAN is used when a program should work as a set of states and switch between them by events.
For example:
- game menus
- character states
- game modes
- network protocols
- event handlers
state Locked {
on open [key == 1] -> Opened
entry {
print("locked\n")
}
}
state Opened {
on close -> Locked
entry {
print("opened\n")
}
}
What happens here:
- The program starts in state
Locked - If event
openarrives and there is a key (key == 1) — transitions toOpened - When entering a state, the code inside
entryis executed
METAL — regular programming
METAL is designed for writing regular code:
- functions
- algorithms
- computations
- memory management
- low-level systems
fn fibonacci(n: i64) -> i64 {
if n <= 1 {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
This code creates a Fibonacci number calculation function.
One language — two approaches
PLAN and METAL use the same B+ syntax but are designed for different tasks:
| Mode | Purpose |
|---|---|
| PLAN | state and event logic |
| METAL | algorithms and systems code |
The compiler determines automatically which mode the code belongs to.
B+ combines the simplicity of high-level languages with the control of systems programming, allowing you to create both game logic and low-level programs.
Table of Contents
- Quick Start
- Compiler Commands
- Language Syntax
- METAL Syntax (New CPU Backend)
- Data Types
- Examples
- BIR Optimizer — Benchmarks & Architecture
- Building from Source
- Project Structure
- License
- Contact
1. Quick Start
Drag a .b+ file onto bpc.bat — it compiles to .exe and runs it.
Or from the command line:
zig\zig-out\bin\bpc.exe run hello.b+
Quick verification that the compiler works
- Create a file
hello.b+in theC:\B-Plusfolder:
state Hello {
entry { print("Hello World!\n") }
}
-
Drag the
hello.b+file ontobpc.bat. -
The compiler will automatically:
- compile the program;
- create a
hello.exefile next to it; - run it immediately.
If hello.exe appeared and the program ran — the compiler is installed and working correctly.
2. Compiler Commands
Syntax
bpc run <input.b+> — compile and run immediately
bpc dll <input.b+> — compile to DLL
bpc build <input.b+> [-o <dir>] — generate C++ UE5 plugin (6 files)
bpc hlsl <input.b+> — generate HLSL shader code
bpc gpu <input.b+> — generate DXIL
bpc cpp <input.b+> — generate C++ code
bpc mir <input.b+> — generate COFF .obj
bpc bpl <input.b+> — lower B+ to BIR and dump
bpc ir <input.b+> — dump BIR pipeline
bpc cfg <input.b+> — dump control flow graph
bpc dom <input.b+> — dump dominator tree
bpc loops <input.b+> — dump loop hierarchy
bpc test <test.bpt> — run test
bpc build <input.b+> [-o <output_dir>]
Generates a C++ UE5 plugin from a B+ file with pipeline description (6 files).
| Step | Description |
|---|---|
| 1 | Reads the entire .b+ file into memory |
| 2 | Parses pipeline description |
| 3 | Generates TSSShaders.h / TSSShaders.cpp |
| 4 | Generates TSSRuntime.h / TSSRuntime.cpp |
| 5 | Generates TSSViewExtension.h / TSSViewExtension.cpp |
| 6 | Writes 6 files to output directory |
If -o is omitted, files are written next to the input file.
Examples:
bpc build pipeline.b+ → 6 C++ files next to pipeline.b+
bpc build pipeline.b+ -o ./Plugin → 6 C++ files in ./Plugin
bpc gpu <input.b+> [-o <output>] / bpc hlsl <input.b+> [-o <output.hlsl>]
Generates HLSL shader code from a B+ file using @bind, @cbuffer, @groupshared annotations. Designed for authoring GPU compute shaders in B+ and compiling them via DXC or FXC.
| Step | Description |
|---|---|
| 1 | Reads the entire .b+ file |
| 2 | Parses source into AST |
| 3 | Parses @bind(kind, reg, format) annotations |
| 4 | Parses @cbuffer(var, cbName, reg, type) annotations |
| 5 | Parses @groupshared(name, size) annotations |
| 6 | Generates HLSL: cbuffers, resource declarations, [numthreads], shader body |
| 7 | Writes output .hlsl file |
Annotations:
// Textures
g_InputColor: @bind(t, 0, float4) // Texture2D<float4> : register(t0)
g_OutputColor: @bind(u, 0, float4) // RWTexture2D<float4> : register(u0)
g_OutputUAV: @bind(u, 1, uint, globallycoherent) // globallycoherent RWTexture2D<uint>
// Sampler
linearClamp: @bind(s, 0) // SamplerState : register(s0)
// Constant buffer
inputSize: @cbuffer(FSR2Constants, 0, float2) // cbuffer FSR2Constants : register(b0) { float2 inputSize; ... }
// Groupshared
sharedMem: @groupshared(sharedMem, 256) // groupshared float sharedMem[256];
Inside entry, for(x, y, w, h) loops translate to uint x = tid.x; if (x >= w) return;. HLSL intrinsics (WaveActiveSum, InterlockedAdd, mad, lerp, etc.) pass through verbatim.
Example:
bpc hlsl fsr2_easu.b+ -o fsr2_easu.hlsl
dxc -T cs_6_6 -E main -Fo fsr2_easu.cso fsr2_easu.hlsl
bpc run <input.b+>
What it does:
| Step | Description |
|---|---|
| 1 | Compiles <input>.exe |
| 2 | Runs the resulting .exe |
| 3 | Captures stdout and prints to console |
| 4 | Returns the program exit code |
Examples:
bpc run traffic.b+ — compiles and runs immediately
bpc run hello.b+ — compiles and runs immediately
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error: invalid args or file not found |
| >0 | Exit code of the compiled program (when using run) |
Notes
- The compiler does not use external assemblers, linkers, or LLVM — all machine code is self-generated.
bpc runcompiles to.exeand runs it immediately.bpc buildgenerates a C++ UE5 plugin (not an .exe).
3. Language Syntax
3.1 States
state <Name> {
...
}
A state is the basic building block. It can contain variables, transitions, and entry/exit blocks.
state Red {
on timer -> Green
entry { print("RED\n") }
}
3.2 Transitions (on)
on <event> -> <TargetState>
When an event (a string from stdin) arrives, the machine transitions to the specified state.
state Green {
on timer -> Yellow
on pedestrian -> Red
}
3.3 Unconditional Transitions (always)
always -> <TargetState>
The transition happens immediately upon entering the state, without waiting for an event.
state Init {
always -> Menu
}
3.4 Entry (entry)
state Door {
entry { print("entered\n") }
on open -> Opened
}
entry { ... } — executed when entering the state.
3.5 Variables
var <name>: <type> [= <value>]
Declared inside a state. Types: int8, int16, int32, int64, u8, u16, u32, u64, byte, bool, short, int, float, etc.
state Counter {
var count: int = 0
on tick -> Self {
count += 1
}
}
Multiple variables can be declared separated by commas:
var x: int, y: int, name: int
Bare identifiers without var and type are also accepted in state bodies:
state S {
x // equivalent to var x: i64
}
Such bare identifiers are automatically registered as i64 variables.
3.6 Assignments
Inside entry { }, exit { } or a transition body:
var x: int
on event -> Next {
x = 42
x += 1
x -= 5
}
Supported operators: =, +=, -=. The right side can use numbers and variable names.
Pointer dereference assignment — when the left side is *<name>, the value is written to the memory address stored in the variable:
*ctl = new_value // MOV [RCX], RAX
result = *ctl // RAX = [RCX]
Used for accessing heap memory via TLS-stored pointers.
3.7 Print (print)
print("string")
Prints a string to stdout. Supports escape sequences \n, \r, \t.
state Hello {
entry { print("Hello, world!\n") }
}
3.8 Export entry
export entry <Name> {
...
}
An exported entry point — compiled as a function visible from outside the DLL. Used when building DLLs (bpc dll) with the -exports flag.
export entry TSS_Init {
print("init\n")
}
3.9 Entry point
entry <Name> {
...
}
An entry point — executed once at program startup.
entry main {
print("Starting...\n")
}
3.10 Enums (enum)
enum <Name> {
Member1,
Member2,
...
}
A global enum declaration.
enum Color {
Red,
Yellow,
Green
}
3.11 Comments
// single-line comment
-- also a comment
4. METAL Syntax
METAL is the second B+ domain (alongside PLAN). Used for functions, variables, structs, pointers, if/while/for, compound assignment.
4.1 Types
| Type | Size (bytes) |
|---|---|
i8 / u8 / bool |
1 |
i16 / u16 |
2 |
i32 / u32 |
4 |
i64 / u64 |
8 |
int |
alias for i64 |
uint |
alias for u64 |
*T |
8 (pointer) |
void |
0 |
4.2 Functions
fn add(a: i64, b: i64) -> i64 {
a + b
}
fn main() {
print_i64(add(3, 4));
}
Last expression is implicit return. Explicit return also works:
fn max(a: i64, b: i64) -> i64 {
if a > b {
return a;
}
return b;
}
4.3 Extern Functions
extern fn print_i64(x: i64);
extern fn read_i64() i64;
extern fn bplus_malloc(size: i64) i64;
extern fn bplus_free(ptr: i64);
extern fn bplus_exit(code: i64);
4.4 Variables
var x: i64 = 42;
var y;
var z = 10;
x = 10;
4.5 Structs
struct Point {
x: i64,
y: i64,
}
Single-line: struct Point { x: i64, y: i64 }
var p: Point;
p.x = 10;
p.y = 20;
print_i64(p.x);
Literals:
var p = Point { x: 10, y: 20 };
var q = Point {
x: 30,
y: 40,
};
4.6 Pointers
var x: i64 = 42;
var p: *i64 = &x;
var y: i64 = *p;
*p = 10;
var addr: *i64 = &p.x;
4.7 If/else
if x > 5 {
print_i64(1);
} else {
print_i64(0);
}
if (x > 5) {
print_i64(1);
}
4.8 While
while i < 3 {
print_i64(i);
i += 1;
}
break / continue:
while i < 10 {
if i == 5 { break; }
if i == 2 { i += 1; continue; }
print_i64(i);
i += 1;
}
4.9 For
for i in 0..10 {
print_i64(i);
}
4.10 Compound Assignment
x += 1;
y -= 5;
z *= 2;
4.11 Operators
| Operator | Description |
|---|---|
* / / |
multiply, divide |
+ / - |
add, subtract |
== / != / > / < / >= / <= |
comparisons |
&& |
logical AND |
\|\| |
logical OR |
4.12 Comments
// single-line comment
4.13 Error Messages
error[UnknownVariable]: test_error.b+:4:1
4 | print_i64(y);
| ^
4.14 CLI
bpc run <input.b+> [-o <output.exe>]
bpc mir <input.b+> [-o <output.obj>]
B+ Source (.b+)
│
┌───────────────┴───────────────┐
│ │
▼ ▼
PLAN Domain METAL Domain
│ │
└───────────────┬───────────────┘
▼
Parser
│
▼
AST
│
▼
HIR
│
▼
BIR (SSA)
│
mem2reg → CFG → SCCP → InstCombine
→ Constant Folding → GVN → LICM
→ Loop Unroll → DCE
│
▼
MIR
│
SSA Destroy → AddrFold → CopyProp
→ Peephole → DCE
│
▼
x64 Backend
│
┌────────────┴────────────┐
▼ ▼
PE (.exe / .dll) COFF (.obj)
5. Data Types
| Type | Size (bytes) |
|---|---|
int8, i8, u8, byte, bool |
1 |
int16, i16, u16, short, half |
2 |
int32, i32, u32, int, uint, float |
4 |
int64, i64, u64 (and anything else) |
8 |
6. Examples
Traffic Light
state Green {
on timer -> Yellow
entry { print("GREEN\n") }
}
state Yellow {
on timer -> Red
entry { print("YELLOW\n") }
}
state Red {
on timer -> Green
entry { print("RED\n") }
}
Input: timer\n cycles through states.
Counter
state Count {
var n: int = 0
on inc -> Self { n += 1 }
on show -> Show
}
state Show {
always -> Count
entry { print("n="); print("?\n") }
}
Guarded Transition
state Door {
on open [key == 1] -> Opened
entry { print("locked\n") }
}
state Opened {
on close -> Door
entry { print("opened\n") }
}
7. BIR Optimizer — Benchmarks & Architecture
Backend Architecture
The compiler backend follows an LLVM-like architecture:
BIR (SSA) ──── MIR (Machine IR) ──── x64 Machine Code
│ │ │
├─ mem2reg ├─ SSA destruction ├─ Linear Scan RA
├─ cfgsimplify ├─ addr_fold (LEA) ├─ Frame Manager
├─ SCCP ├─ copy propagation ├─ Instruction Encoder
├─ InstCombine ├─ DCE └─ COFF/PE
├─ ConstantFolding └─ peephole
├─ GVN
├─ LICM
├─ Unroll
└─ DCE
BIR Optimization Pipeline
| Pass | Description |
|---|---|
| mem2reg | Promote memory to SSA registers |
| cfgsimplify | Control flow graph simplification |
| SCCP | Sparse conditional constant propagation |
| InstCombine | Algebraic identities, comparison folding |
| ConstantFolding | Constant expression evaluation, max/min |
| GVN | Global value numbering (CSE for commutative ops) |
| LICM | Loop-invariant code motion |
| Unroll | Short loop unrolling |
| DCE | Dead code elimination |
MIR Optimization Pipeline
| Pass | Description |
|---|---|
| SSA Destruction | Replace phi with mov via CopyProp |
| AddrFold | LEA synthesis from address arithmetic [base+index*scale+disp] |
| Copy Propagation | Copy propagation (3 iterations) |
| DCE | Dead code elimination |
| Peephole | Constant folding, comparison optimization |
Optimization Benchmarks
| Test | Without opts | With opts | Savings |
|---|---|---|---|
| P1: Arithmetic chain (mul/div pow2, mul -1) | 91 B, 14 instrs | 51 B, 10 instrs | 44.0% |
| P2: Dead branch (SCCP: if(true) → else) | 74 B, 8 instrs, 4 blocks | — 4 instrs, 2 blocks | branch eliminated |
| P3: Redundant CSE (5+3 computed 3×) | 74 B, 8 instrs | 26 B, 2 instrs | 64.9% |
| P4: Stress (200 vregs, chain of adds) | 2026 B, 402 instrs | 26 B, 2 instrs | 98.7% |
| P5: max/min constant folding | 107 B, 8 instrs | 26 B, 2 instrs | 75.7% |
E2E Tests
25 codegen E2E tests (BIR → MIR → x64 → execute):
- Integer arithmetic: add, sub, mul, div, neg, not, and/or/xor
- Branching: if/else, phi nodes
- Stress: 200 and 500 vregs, spills
- Strength reduction: mul→shl, div→shr, mul -1→neg
- SCCP: dead branches, constant folding
- InstCombine: double neg, add/sub cancellation
- Floating point: f32/f64 add, mul, sub, div, neg
- Conversions: int↔float, sext, zext, trunc
- min/max: CMOVcc (branchless)
8. Building from Source
Requires Zig (master, >= 0.14).
cd zig
zig build
Or directly:
cd zig
zig build-exe src/main.zig -femit-bin=bpc.exe
After building:
bpc.exe run example.b+
9. Project Structure
Compiler Architecture
Frontend (parser, AST, semantic analysis)
│
▼
HIR (High-Level IR) — BIR SSA
│ mem2reg → cfgsimplify → SCCP → InstCombine → ConstantFolding
│ → GVN → Unroll → LICM → ForwardStoreToLoad → DeadStoreElimination → DCE
▼
MIR (Machine IR) — target-independent
│ SSA Destroy → AddrFold → CopyProp → Peephole ×3 → DCE
▼
Targets (code generation)
│ ISEL → RegAlloc → Encoding
▼
Object (PE/COFF → .exe)
Source Tree
The compiler source is organized into four layers:
- Frontend (
compiler/frontend/) — parser, AST, semantic analysis - Middle (
compiler/middle/bir/) — BIR core, optimizer, analysis, verification - Backend (
compiler/backend/mir/,targets/) — MIR optimization, x64 code generation - GPU (
compiler/gpu/) — GPU shader compilation (HLSL, DXIL)
10. License
MIT License
MIT License
Copyright (c) 2025 bylka2W
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
11. Contact
- GitHub: github.com/bylka2W
- Repository: github.com/bylka2W/B-Plus
- GitVerse: gitverse.ru/bylka2W/B-Plus
- GitFlic: gitflic.ru/project/bylka2w/b-plus
- GitLab: gitlab.com/bylka2W/b-plus
- Author: bylka2W