Skip to content

Commit d82a94b

Browse files
committed
Bootstrap ternary plugin: types, lowering pass, runtime helpers
1 parent 18ff7fd commit d82a94b

23 files changed

Lines changed: 2626 additions & 532 deletions

.github/workflows/ci.yml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
gcc-version: 15
2323

2424
steps:
25-
- uses: actions/checkout@v2
25+
- uses: actions/checkout@v4
2626

2727
- name: Install GCC ${{ matrix.gcc-version }}
2828
if: matrix.os == 'ubuntu-latest'
@@ -39,6 +39,20 @@ jobs:
3939
echo "CC=gcc-${{ matrix.gcc-version }}" >> $GITHUB_ENV
4040
echo "CXX=g++-${{ matrix.gcc-version }}" >> $GITHUB_ENV
4141
42+
- name: Build runtime
43+
run: make runtime
44+
45+
- name: Build plugin
46+
run: make plugin
47+
4248
- name: Run Tests
49+
run: make test
50+
51+
- name: Install
52+
run: sudo make install
53+
54+
- name: Test installation
4355
run: |
44-
make test
56+
echo '#include <ternary.h>' > test_install.c
57+
echo 'int main() { return 0; }' >> test_install.c
58+
$CC -I/usr/local/include/ternary -c test_install.c -o test_install.o

CMakeLists.txt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,37 @@ if(NOT GCC_PLUGIN_INCLUDE_DIR)
1515
message(FATAL_ERROR "GCC plugin include directory not found")
1616
endif()
1717

18+
# Find GMP
19+
find_path(GMP_INCLUDE_DIR
20+
NAMES gmp.h
21+
PATHS /opt/homebrew/include
22+
/usr/include
23+
/usr/local/include
24+
)
25+
26+
if(NOT GMP_INCLUDE_DIR)
27+
message(FATAL_ERROR "GMP include directory not found")
28+
endif()
29+
30+
find_library(GMP_LIBRARY
31+
NAMES gmp
32+
PATHS /opt/homebrew/lib
33+
/usr/lib
34+
/usr/local/lib
35+
)
36+
37+
if(NOT GMP_LIBRARY)
38+
message(FATAL_ERROR "GMP library not found")
39+
endif()
40+
1841
include_directories(${GCC_PLUGIN_INCLUDE_DIR})
42+
include_directories(${GMP_INCLUDE_DIR})
1943
include_directories(include)
2044

2145
# Plugin
2246
add_library(ternary_plugin SHARED src/ternary_plugin.cpp)
2347
target_compile_definitions(ternary_plugin PRIVATE PIC)
24-
target_link_libraries(ternary_plugin -Wl,-undefined,dynamic_lookup)
48+
target_link_libraries(ternary_plugin ${GMP_LIBRARY} -Wl,-undefined,dynamic_lookup)
2549

2650
# Runtime
2751
add_library(ternary_runtime STATIC runtime/ternary_runtime.c)

README.md

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ This is a GCC plugin and helper ABI for a balanced-ternary ISA. It analyzes tern
44
expressions in C/C++ and can lower them to helper calls that map to ternary ISA
55
instructions.
66

7+
**Note:** This project is a passion/research effort exploring ternary logic, which has theoretical advantages in arithmetic and AI contexts. It is currently at an early stage, compiling and running toy examples, rather than a mature toolchain component.
8+
79
The plugin supports:
8-
- Packed ternary types: `t6_t`, `t12_t`, `t24_t` (6, 12, 24 trits; 2-bit packed encoding)
10+
- Packed ternary types: `t32_t`, `t64_t`, `t128_t` (32/64/128 trits; 2-bit packed encoding)
911
- Extended arithmetic operations: add, sub, mul, div, mod, neg
1012
- Logic operations: not
1113
- Comparison operations: cmp (returns -1, 0, +1)
@@ -19,10 +21,10 @@ described in `SPECIFICATION.md`.
1921

2022
Balanced ternary offers symmetric range and simpler arithmetic compared to two's complement binary.
2123

22-
| Aspect | Balanced Ternary (e.g., 12 trits, 24 bits) | Two's Complement (e.g., 24-bit int) |
23-
|--------|--------------------------------------------|-------------------------------------|
24-
| Range | -531441 to 531441 | -8388608 to 8388607 |
25-
| Dynamic Range | Higher for same bits (3^12 vs 2^24) | Standard binary range |
24+
| Aspect | Balanced Ternary (e.g., 32 trits, 64 bits) | Two's Complement (e.g., 64-bit int) |
25+
|--------|-------------------------------------------|-------------------------------------|
26+
| Range | -926510094425920 to 926510094425920 | -9223372036854775808 to 9223372036854775807 |
27+
| Dynamic Range | 3^32 vs 2^64 | Standard binary range |
2628
| Rounding | Symmetric (no bias toward positive/negative) | Biased toward negative |
2729
| Arithmetic Simplicity | Easier carry-free ops in hardware | Complex carry propagation |
2830

@@ -48,6 +50,8 @@ make install
4850

4951
This installs to `/usr/local/lib` and `/usr/local/include/ternary` by default. Adjust `DESTDIR` if needed.
5052

53+
**Note:** On macOS, Apple Clang does not support GCC plugins. Use Homebrew GCC (e.g., `gcc-14` or `gcc-15`) instead.
54+
5155
### macOS Users - Important!
5256

5357
Apple Clang (even when invoked as `gcc`) does not support GCC plugins. You may
@@ -133,8 +137,8 @@ Optional arguments:
133137
`__builtin_ternary_shl`, `__builtin_ternary_shr`, `__builtin_ternary_rol`, and `__builtin_ternary_ror`.
134138
- `-fplugin-arg-ternary_plugin-conv` enables lowering of ternary conversion builtins like
135139
`__builtin_ternary_tb2t`, `__builtin_ternary_tt2b`, `__builtin_ternary_t2f`, and `__builtin_ternary_f2t`.
136-
- `-fplugin-arg-ternary_plugin-types` enables builtin ternary integer types `t6_t`, `t12_t`,
137-
`t24_t`, `t48_t`, `t96_t`, `t192_t` with packed 2-bit trit storage.
140+
- `-fplugin-arg-ternary_plugin-types` enables builtin ternary integer types `t32_t`, `t64_t`,
141+
`t128_t` with packed 2-bit trit storage.
138142
- `-fplugin-arg-ternary_plugin-prefix=<name>` sets the function name prefix used by `-lower`
139143
(default: `__ternary_select`).
140144

@@ -161,8 +165,9 @@ For testing without the plugin, the header provides C implementations of all ter
161165
Packed ternary types use a 2-bit encoding per trit (00 = -1, 01 = 0, 10 = +1).
162166
Define `TERNARY_USE_BUILTIN_TYPES` before including the header when compiling with
163167
`-fplugin-arg-ternary_plugin-types` to avoid typedef conflicts.
164-
Helper/runtime support is currently provided for t6/t12/t24 only; t48/t96/t192 require
165-
custom runtime implementations.
168+
Helper/runtime support is currently provided for t32/t64 only; t128 requires
169+
custom runtime implementations. The reference runtime uses 64-bit logical
170+
conversion helpers, so t64 correctness is limited to values that fit in int64.
166171

167172
For example:
168173

@@ -185,15 +190,48 @@ gcc -fplugin=./ternary_plugin.so -fplugin-arg-ternary_plugin-lower -Iinclude -c
185190

186191
For a minimal out-of-line runtime, use the reference implementation in `runtime/ternary_runtime.c`
187192
with the public header `include/ternary_runtime.h`. It implements the same packed 2-bit-trit
188-
semantics as the helpers (t6/t12/t24) and is intended as a starting point for a real ISA-backed
189-
library. Packed helpers for t48/t96/t192 are not provided in this reference runtime.
193+
semantics as the helpers (t32/t64) and is intended as a starting point for a real ISA-backed
194+
library. Packed helpers for t128 are not provided in this reference runtime.
190195

191196
Example build:
192197

193198
```bash
194199
cc -Iinclude -c runtime/ternary_runtime.c -o ternary_runtime.o
195200
```
196201

202+
The `runtime_skeleton/` directory includes a tiny standalone helper set plus a sanity-check
203+
test you can build and run:
204+
205+
```bash
206+
mkdir -p build
207+
cc -Iruntime_skeleton/include runtime_skeleton/src/ternary_runtime_skeleton.c \
208+
runtime_skeleton/test_runtime_skeleton.c -o build/runtime_skeleton_test
209+
./build/runtime_skeleton_test
210+
```
211+
212+
## Godbolt Recipe (Local-Equivalent)
213+
214+
Godbolt does not allow custom GCC plugins, so use the local equivalent command line to reproduce
215+
what you would run in Compiler Explorer with a plugin-enabled GCC build:
216+
217+
```bash
218+
cc -fplugin=./ternary_plugin.so \
219+
-fplugin-arg-ternary_plugin-types \
220+
-fplugin-arg-ternary_plugin-lower \
221+
-fplugin-arg-ternary_plugin-arith \
222+
-fplugin-arg-ternary_plugin-logic \
223+
-fplugin-arg-ternary_plugin-cmp \
224+
-fplugin-arg-ternary_plugin-shift \
225+
-fplugin-arg-ternary_plugin-conv \
226+
-Iinclude -c examples/ternary_basic.c -o build/ternary_basic.o
227+
```
228+
229+
Link with the reference runtime object to satisfy helper symbols:
230+
231+
```bash
232+
cc build/ternary_basic.o runtime/ternary_runtime.o -o build/ternary_basic
233+
```
234+
197235
## Testing
198236

199237
Use the provided test file:
@@ -216,12 +254,23 @@ make test CXX=g++-15 CC=gcc-15
216254
This plugin analyzes ternary conditional expressions in the code and can optionally
217255
lower ternary operations to helper calls suitable for targeting a balanced-ternary ISA.
218256

257+
## Balanced Ternary Literals
258+
259+
Use balanced-ternary strings to construct packed values:
260+
261+
```c
262+
t32_t a = T32_BT_STR("1 0 -1 1");
263+
t64_t b = T64_BT_STR("1,0,0,-1");
264+
```
265+
266+
The parser consumes trits from left to right (most significant to least significant).
267+
219268
## Known Limitations
220269

221270
- No auto-vectorization for ternary operations yet.
222271
- Performance is reference implementation; optimized ternary hardware would excel.
223272
- Limited interaction testing with GCC optimizations (-O3 may affect lowering).
224-
- No support for ternary literals or advanced syntax beyond builtins.
273+
- No native literal syntax; use the balanced-ternary string macros.
225274
- Plugin requires GCC with plugin support (not available in all distributions).
226275

227276
## Examples

SPECIFICATION.md

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,32 @@ reinterpretation of the same trit pattern with a different comparison rule.
5151

5252
The ISA defines ternary integer types by trit width:
5353

54-
- t6, t12, t24, t48, t96, t192 (suggested), with platform-specific availability.
54+
- t32, t64, t128 (initial focus), with platform-specific availability.
5555

5656
The toolchain maps these to C integer types via a storage ABI that packs trits into
5757
binary containers. A recommended mapping using 2-bit trit packing is:
5858

59-
- t6 -> stored in 16-bit container (12 bits used)
60-
- t12 -> stored in 32-bit container (24 bits used)
61-
- t24 -> stored in 64-bit container (48 bits used)
62-
- t48 -> stored in 128-bit container (96 bits used)
59+
- t32 -> stored in 64-bit container
60+
- t64 -> stored in 128-bit container
61+
- t128 -> stored in 256-bit container
6362

6463
Packing is logical; the physical encoding is an ABI detail. Compilers must preserve
6564
bit patterns across loads/stores and calls.
6665

66+
### Source Literal Helpers
67+
68+
The current toolchain does not define a native ternary literal token. Instead, it
69+
provides helpers that parse balanced-ternary strings left-to-right (most significant
70+
trit first). Example:
71+
72+
```c
73+
t32_t a = T32_BT_STR("1 0 -1 1");
74+
t64_t b = T64_BT_STR("1,0,0,-1");
75+
```
76+
77+
The runtime parser accepts `-1`, `0`, `1`, or `+1` tokens separated by whitespace or
78+
commas. Invalid strings return zero.
79+
6780
### Boolean and Condition Types
6881

6982
Condition values are trits. Comparisons produce {-1, 0, +1}. Branches and selects
@@ -77,6 +90,15 @@ decoded with tt2b first.
7790
IEEE-754 binary float and double are preserved. Ternary select supports floats, but
7891
ternary arithmetic is limited to integer types for now.
7992

93+
### Type Promotion and Conversion Rules
94+
95+
Ternary types follow C-like promotion rules for operations:
96+
97+
- Usual arithmetic conversions: smaller types promote to larger (t32 -> t64 -> t128).
98+
- Mixed ternary/binary: ternary promotes to binary container size for compatibility.
99+
- Condition handling: ternary conditions are ternary_cond_t (int8_t with trit values).
100+
- Conversions: explicit casts use helper functions; implicit conversions require plugin lowering.
101+
80102
## Instruction Semantics
81103

82104
### Arithmetic
@@ -90,10 +112,9 @@ Ternary logic is defined with min/max semantics:
90112

91113
- tand(a,b) = min(a,b)
92114
- tor(a,b) = max(a,b)
93-
- txor(a,b) = a + b - 2 * min(a,b) - 2 * max(a,b) (ISA may choose alternative ternary XOR)
115+
- txor(a,b) = 0 if a == b, else -a if b == 0, else -b if a == 0, else 0
94116

95-
Implementations may choose a different ternary XOR definition, but it must be
96-
documented and stable.
117+
This definition is stable and documented.
97118

98119
### Comparisons
99120

@@ -176,8 +197,8 @@ but operand formats and semantics are fully specified. The ISA operates on packe
176197
- Fn: IEEE floating-point register (f32/f64).
177198
- Imm: immediate value (binary-encoded).
178199

179-
Ternary registers have a fixed trit width per instruction variant (e.g., t6, t12,
180-
t24, t48, t96, t192). The width is encoded in the mnemonic suffix.
200+
Ternary registers have a fixed trit width per instruction variant (e.g., t32, t64,
201+
t128). The width is encoded in the mnemonic suffix.
181202

182203
### Register and Instruction Formats
183204

@@ -190,6 +211,18 @@ The base ISA assumes a three-operand format for ternary ALU operations:
190211

191212
Where:
192213
- Td/Ta/Tb/Tc are ternary registers of width tN.
214+
215+
## Roadmap / Next Steps
216+
217+
Priority | Suggestion | Why it helps | Status
218+
--- | --- | --- | ---
219+
High | Finish/clean lowering for common ops (add/sub/mul/neg/cmp/select/conv) | Lets users write straightforward ternary arithmetic without manual loops | In progress
220+
High | Small portable runtime lib for t32/t64 | Enables real programs and benchmarking | Implemented (reference runtime in `runtime/`)
221+
Medium | Trit-count-aware constant folding in plugin | Allows constants like `t32_t x = 42` to fold when representable | In progress
222+
Medium | Better ternary type creation and user-facing syntax (e.g., `__ternary(32)` or attributes) | Easier adoption | Planned
223+
Medium | Dump stats on how many ternary ops survive to RTL/assembly | Quantifies remaining work for hardware targets | Planned
224+
Low | Larger trit counts (t256+) via limb-based runtime emulation | Future-proofs larger types | Planned
225+
Low | Branch-free ternary↔binary conversion helpers | Improves emulation speed | Planned
193226
- Rd is a binary integer register holding {-1,0,+1} in two's complement.
194227

195228
### Instruction Semantics
@@ -311,6 +344,12 @@ macros to decode promotions for packed ternary types.
311344
- __builtin_ternary_or(a, b)
312345
- __builtin_ternary_xor(a, b)
313346
- __builtin_ternary_cmp(a, b)
347+
- __builtin_ternary_eq(a, b) -> int
348+
- __builtin_ternary_ne(a, b) -> int
349+
- __builtin_ternary_lt(a, b) -> int
350+
- __builtin_ternary_le(a, b) -> int
351+
- __builtin_ternary_gt(a, b) -> int
352+
- __builtin_ternary_ge(a, b) -> int
314353
- __builtin_ternary_shl(a, b)
315354
- __builtin_ternary_shr(a, b)
316355
- __builtin_ternary_rol(a, b)
@@ -323,7 +362,7 @@ macros to decode promotions for packed ternary types.
323362
### Custom Types
324363

325364
When `-fplugin-arg-ternary_plugin-types` is enabled, the plugin registers builtin
326-
types `t6_t`, `t12_t`, `t24_t`, `t48_t`, `t96_t`, and `t192_t` with packed 2-bit
365+
types `t32_t`, `t64_t`, and `t128_t` with packed 2-bit
327366
trit storage (precision = trit_count * 2).
328367

329368
### Helper ABI
@@ -344,6 +383,12 @@ Helper functions implement ISA-visible operations in C:
344383
- __ternary_or(a, b)
345384
- __ternary_xor(a, b)
346385
- __ternary_cmp(a, b)
386+
- __ternary_eq(a, b) -> int
387+
- __ternary_ne(a, b) -> int
388+
- __ternary_lt(a, b) -> int
389+
- __ternary_le(a, b) -> int
390+
- __ternary_gt(a, b) -> int
391+
- __ternary_ge(a, b) -> int
347392
- __ternary_shl(a, b)
348393
- __ternary_shr(a, b)
349394
- __ternary_rol(a, b)
@@ -366,7 +411,7 @@ ternary_cond_t before calling helpers. The reference helper header uses a packed
366411

367412
## Testing and Validation
368413

369-
- Ternary select: int, unsigned, float, double, and custom ternary types (t6_t, t12_t, t24_t).
414+
- Ternary select: int, unsigned, float, double, and custom ternary types (t32_t, t64_t).
370415
- Arithmetic builtins: add, sub, mul, div, mod, neg.
371416
- Logic builtins: not.
372417
- Comparison builtins: cmp (returns -1, 0, +1).
@@ -384,14 +429,14 @@ ternary_cond_t before calling helpers. The reference helper header uses a packed
384429
- Comparison builtin lowering (cmp)
385430
- Helper function ABI with packed C implementations for testing
386431
- Test suite covering all implemented operations
387-
- Builtin ternary type registration (t6_t, t12_t, t24_t, t48_t, t96_t, t192_t)
388-
- Shift/rotate builtin lowering (t6/t12/t24)
432+
- Builtin ternary type registration (t32_t, t64_t, t128_t)
433+
- Shift/rotate builtin lowering (t32/t64)
389434
- Conversion builtin lowering (tb2t, tt2b, t2f, f2t)
390435

391436
### Known Limitations
392437
- GCC 15 API compatibility may still require adjustments
393-
- Packed ternary helpers currently cover t6/t12/t24 only (no big-int support for t48+)
394-
- Reference runtime/helpers do not implement t48/t96/t192 operations
438+
- Packed ternary helpers currently cover t32/t64 only (no big-int support for t128)
439+
- Reference runtime/helpers do not implement t128 operations
395440

396441
## Future Extensions
397442

@@ -406,9 +451,9 @@ ternary_cond_t before calling helpers. The reference helper header uses a packed
406451
## Still Needs Implementation
407452

408453
1. Custom ternary types
409-
- Implemented: builtin types t6, t12, t24, t48, t96, t192 (packed 2-bit trits)
454+
- Implemented: builtin types t32, t64, t128 (packed 2-bit trits)
410455
- Implemented: storage ABI for 2-bit trit packing
411-
- Remaining: helper/runtime support for t48/t96/t192 (requires big-int support)
456+
- Remaining: helper/runtime support for t128 (requires big-int support)
412457
2. Extended arithmetic operations
413458
- Implemented: tsub, tdiv, tmod, tneg builtins and helpers
414459
- Remaining: validation coverage for edge cases and larger widths
@@ -419,10 +464,10 @@ ternary_cond_t before calling helpers. The reference helper header uses a packed
419464
- Implemented: tcmp builtins and helpers (-1/0/+1)
420465
- Remaining: validation coverage for edge cases and larger widths
421466
5. Shifts and rotates
422-
- Implemented: trit-based shl/shr/rol/ror builtins and helpers (t6/t12/t24)
467+
- Implemented: trit-based shl/shr/rol/ror builtins and helpers (t32/t64)
423468
- Remaining: validation coverage and larger width support
424469
6. Type conversions
425-
- Implemented: tb2t/tt2b and t2f/f2t builtins and helpers (t6/t12/t24)
470+
- Implemented: tb2t/tt2b and t2f/f2t builtins and helpers (t32/t64)
426471
- Remaining: precise rounding semantics for larger widths
427472
7. Full ISA integration
428473
- Missing: Complete mapping to all specified ISA instructions
@@ -433,6 +478,6 @@ ternary_cond_t before calling helpers. The reference helper header uses a packed
433478

434479
## Priority Order
435480

436-
- High: Helper support for larger ternary widths (t48/t96/t192)
481+
- High: Helper support for larger ternary widths (t128)
437482
- Medium: Varargs ABI validation tests for ternary types
438483
- Low: Optional hardware ISA support for ternary mnemonics

0 commit comments

Comments
 (0)