Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions .trinity/seals/conformance_T27bConformanceRemInt.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
{
"built_by": "t27c-bootstrap@0.5.2+8ea472041",
"built_by": "t27c-bootstrap@0.5.2+14d7fb77d",
"closure": [],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:fba2bc4ca42582b8fe1d740617af2bb690a604e6da93565cfb8027f512f95230",
"gen_hash_rust": "sha256:e1d78592d7020117c806c51d4f0eb88cd26f3a4e76b9357f465d7ca9146b7f86",
"gen_hash_verilog": "sha256:1d225dd532cb58ab45c4196116bd9a5a9e15f382a97a3d861b98501c9a61fba2",
"gen_hash_zig": "sha256:5bb64df244d6d26188a53c9e2503d26ddca665324ce2185142489298d21517ad",
"gen_hash_c": "sha256:4b66c90aff70fb7ee7a5d08216a5b9426d4eca75c485ee2cccd7f214bc5d09da",
"gen_hash_rust": "sha256:0902e5d88c053df87da007518ceba6dc625e01f3aa5b8643122e0393930a9ded",
"gen_hash_verilog": "sha256:a8028c37c484c21e32959ca4abf7b752d092d7dd701bc25c71ca919127ae0451",
"gen_hash_zig": "sha256:bb256b4c229ac55b8f5d564d2d170233421e6b4db60b4dcab0aa38f4d74a66ee",
"module": "T27bConformanceRemInt",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T11:14:01Z",
"sealed_at": "2026-10-18T11:31:39Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:6cb6dc2002cab87fb3dd6010105291dee503677b6851a9c74d29ab58def11fe3",
"spec_hash": "sha256:f26be979bdc438b926d06c1b97ce4142a9d61403007b6547cc42557fd3b3e44c",
"spec_path": "specs/tri/t27b/conformance/rem_int.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 6,
"total": 6,
"passed": 8,
"total": 8,
"vacuous": 0
},
"toolchain": {
Expand Down
18 changes: 9 additions & 9 deletions .trinity/seals/t27b_T27bLibmPlan.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
{
"built_by": "t27c-bootstrap@0.5.2+8ea472041",
"built_by": "t27c-bootstrap@0.5.2+14d7fb77d",
"closure": [],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:bba02f9b9f4a1572d001f52952d7378e102e7502e5ef8bfe61b0329c780615b9",
"gen_hash_rust": "sha256:aea1217193961c1c858a2545c41e5eb8050bd320abc98904d5bc9ad05c60aeb0",
"gen_hash_verilog": "sha256:9d337661883412424964dc7f532d2edaaedd6b7543198f63a77c757e459ab3e5",
"gen_hash_zig": "sha256:10892615534ebe25a212d555d7d4c4ee0726c3b9dc72f2ca29a3518a2e3e20c0",
"gen_hash_c": "sha256:ddbefccfc3ff17740b9096bdf66caa211e86530a6432d8786e194b5b6369bf60",
"gen_hash_rust": "sha256:0f41ef8a8f00b2c4e2552ff4e1877c7e994ff2bca05c7816c87e27699c996d38",
"gen_hash_verilog": "sha256:51bec45a636988c99680b4c01136d653ee4173927d7415c8bfffd28234ab6242",
"gen_hash_zig": "sha256:51ea94cdba1ba50cea9fe4d30fe52a35a9ca8b1d1a326e9b678bf734d9351a8b",
"module": "T27bLibmPlan",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T11:14:00Z",
"sealed_at": "2026-10-18T11:31:40Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:cf0b297711d7619d177ff2089aabcbf7dd8674adfea4c4062d1a9ed9b42ea229",
"spec_hash": "sha256:4b13a35b1cf27ee2de1ffd5104870fa57c4e76efcdc09d6e616909081b222a94",
"spec_path": "specs/tri/t27b/libm_plan.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 16,
"total": 16,
"passed": 17,
"total": 17,
"vacuous": 0
},
"toolchain": {
Expand Down
13 changes: 6 additions & 7 deletions cli/t27b/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3807,7 +3807,7 @@ impl<'a> Lower<'a> {
}
}

/// A builtin of libm_plan.t27 (`@exp`, `@log`, an i64 `@mod`; an integer `@rem` is t27b's `%`): a call of its routine from libm.t27, compiler_rt's
/// A builtin of libm_plan.t27 (`@exp`, `@log`, a signed `@mod`; an integer `@rem` is t27b's `%`): a call of its routine from libm.t27, compiler_rt's
/// algorithm, so the bits are the reference's, folded or not. What is refused instead is the plan's.
fn libm_call(&mut self, n: &Node) -> R<Val> {
self.see(n);
Expand All @@ -3829,12 +3829,11 @@ impl<'a> Lower<'a> {
match (v, self.sigs.get(xp::routine(b, a)).filter(|s| !s.poisoned).map(|s| (s.id, s.ret.clone()))) {
(v, _) if a == xp::OPERATOR => { let r = self.expr(&n.children[1])?; self.binary(xp::OPERATOR_TEXT, v, r) }
(v, Some((func, Some(LTy::S(ty))))) if t || matches!(v, Val::E(_) | Val::Cf(..)) => {
let mut args = if t { vec![] } else { vec![self.coerce(v, ty0)?] }; // `@log(10.0)`: the literal, one rounding to f64
for c in &n.children[at + 1..] {
let r = self.expr_as(c, &LTy::S(ty0))?; // `@rem(x, 2.0)`, `@mod(i, 2147483648)`: the first one's type
args.push(self.reg(r)?);
}
Ok(Val::E(Expr { ty, kind: ExprKind::Call { func, args } }))
let w = |e: Expr| if xp::widens(a) { Expr { ty, kind: ExprKind::Widen(Box::new(e)) } } else { e }; // an i32 `@mod`: to the i64 routine's type
let mut args = if t { vec![] } else { vec![w(self.coerce(v, ty0)?)] }; // `@log(10.0)`: the literal, one rounding to f64
for c in &n.children[at + 1..] { let r = self.expr_as(c, &LTy::S(ty0))?; args.push(w(self.reg(r)?)); } // `@rem(x, 2.0)`, `@mod(i, 2147483648)`: the first one's type
let call = Expr { ty, kind: ExprKind::Call { func, args } }; // and back to the operands' type, which holds 0 <= r < b
Ok(Val::E(if xp::widens(a) { Expr { ty: ty0, kind: ExprKind::Cast { arg: Box::new(call), site: 0 } } } else { call }))
}
_ => self.reject(xp::what(b), format!("`{}`: {}", n.name, xp::why(a))),
}
Expand Down
6 changes: 3 additions & 3 deletions docs/reports/t27b_expectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"reference-bug": "the reference passes it for the wrong reason; t27b is right not to",
"n/a": "outside what t27b is for (say why in the PR that sets it)"
},
"max_not_pass": 77,
"max_not_pass": 75,
"entries": [
{"path": "specs/a/b_c.t27", "t27b": "pass"},
{"path": "specs/a_b/c.t27", "t27b": "pass"},
Expand Down Expand Up @@ -448,8 +448,8 @@
{"path": "specs/ml/activation/silu_swish_activation.t27", "t27b": "blocked", "blocker": "ExprCall(undeclared fn)", "reason": "unimplemented"},
{"path": "specs/ml/activation/softmax.t27", "t27b": "blocked", "blocker": "ExprCall(undeclared fn)", "reason": "unimplemented"},
{"path": "specs/ml/activation/tanh_activation.t27", "t27b": "blocked", "blocker": "ExprCall(undeclared fn)", "reason": "unimplemented"},
{"path": "specs/ml/arena_heap.t27", "t27b": "blocked", "blocker": "ExprCall(@mod)", "reason": "unimplemented"},
{"path": "specs/ml/file_io_contract.t27", "t27b": "blocked", "blocker": "ExprCall(@mod)", "reason": "unimplemented"},
{"path": "specs/ml/arena_heap.t27", "t27b": "pass"},
{"path": "specs/ml/file_io_contract.t27", "t27b": "pass"},
{"path": "specs/ml/host_io_model.t27", "t27b": "blocked", "blocker": "ExprCall(undeclared fn)", "reason": "unimplemented"},
{"path": "specs/ml/igla_champion_capsule.t27", "t27b": "blocked", "blocker": "ExprIdentifier(undeclared)", "reason": "unimplemented"},
{"path": "specs/ml/layers/avgpool2d_layer.t27", "t27b": "pass"},
Expand Down
22 changes: 18 additions & 4 deletions gen/rust/tri/t27b/libm_plan.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Generated from .t27 spec
// DO NOT EDIT — generated by t27c
// NOT LOWERED BY THIS BACKEND: 16 test(s), 0 invariant(s), 0 function(s) with no body.
// NOT LOWERED BY THIS BACKEND: 17 test(s), 0 invariant(s), 0 function(s) with no body.
// This backend emits declarations only. The spec's checks live in
// the Zig and Verilog outputs; do not read this file as verified.

Expand Down Expand Up @@ -62,6 +62,8 @@ pub const K_UINT: u8 = 5;

pub const K_I64: u8 = 6;

pub const K_WIDE: u8 = 7;

pub const CALL_F64: u8 = 0;

pub const CALL_F32: u8 = 1;
Expand All @@ -88,6 +90,12 @@ pub const CALL_I64: u8 = 10;

pub const REFUSE_WIDTH: u8 = 11;

pub const CALL_WIDE: u8 = 12;

pub fn widens(act: u8) -> bool {
return (act == CALL_WIDE);
}

pub fn kind(ty: &'static str) -> u8 {
if ((ty.len() < 2) || (((ty.as_bytes()[0] != 105) && (ty.as_bytes()[0] != 117)) && (ty.as_bytes()[0] != 102))) {
return K_OTHER;
Expand All @@ -105,6 +113,9 @@ pub fn kind(ty: &'static str) -> u8 {
if ((((ty.len() == 3) && (ty.as_bytes()[0] == 105)) && (ty.as_bytes()[1] == 54)) && (ty.as_bytes()[2] == 52)) {
return K_I64;
}
if ((ty.as_bytes()[0] == 105) && ((ty.len() > 3) || ((ty.len() == 3) && ((ty.as_bytes()[1] > 54) || ((ty.as_bytes()[1] == 54) && (ty.as_bytes()[2] > 52)))))) {
return K_WIDE;
}
if (ty.as_bytes()[0] != 102) {
return K_INT;
}
Expand Down Expand Up @@ -166,7 +177,7 @@ pub fn plan(b: u8, operands: usize, k: u8) -> u8 {
if (operands != arity(b)) {
return REFUSE_ARITY;
}
if ((((k == K_INT) || (k == K_UINT)) || (k == K_I64)) && (b == REM)) {
if (((((k == K_INT) || (k == K_UINT)) || (k == K_I64)) || (k == K_WIDE)) && (b == REM)) {
return OPERATOR;
}
if ((k == K_UINT) && (b == MOD)) {
Expand All @@ -176,6 +187,9 @@ pub fn plan(b: u8, operands: usize, k: u8) -> u8 {
return CALL_I64;
}
if ((k == K_INT) && (b == MOD)) {
return CALL_WIDE;
}
if ((k == K_WIDE) && (b == MOD)) {
return REFUSE_WIDTH;
}
if ((k == K_LITERAL) && (b == LOG)) {
Expand Down Expand Up @@ -248,7 +262,7 @@ pub fn routine(b: u8, act: u8) -> &'static str {
if ((b == CEIL) && (act == CALL_F64)) {
return "__t27b_libm_ceil";
}
if ((b == MOD) && (act == CALL_I64)) {
if ((b == MOD) && ((act == CALL_I64) || (act == CALL_WIDE))) {
return "__t27b_libm_mod";
}
if ((b == ROUND) && (act == CALL_F32)) {
Expand Down Expand Up @@ -377,7 +391,7 @@ pub fn why(act: u8) -> &'static str {
return "its type operand is not its value's type";
}
if (act == REFUSE_WIDTH) {
return "a signed `@mod` is ported for i64 only";
return "a signed `@mod` wider than i64 is not ported (no i128 routine)";
}
return "its routine in specs/tri/t27b/libm.t27 did not lower";
}
Expand Down
86 changes: 85 additions & 1 deletion specs/tri/t27b/conformance/rem_int.t27
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
; of signed and unsigned operands, with a literal beside a typed integer taking that integer's type.
; - `@mod(a, b)` is the floored remainder (#10405): the sign of the divisor, so 0 <= @mod(a, b) < b, of an
; i64 (a call of specs/tri/t27b/libm.t27's `t27b_libm_mod`) and of an unsigned integer (`%`), with
; specs/t27f/reel_particles.t27's `lcg` as written. t27b ports a signed `@mod` of i64 only, so no i32 one here.
; specs/t27f/reel_particles.t27's `lcg` as written. A narrower signed `@mod` (#10423: i32, i16, i8, i7) widens to
; that i64 routine and narrows back, as specs/tri/t27b/libm_plan.t27 says; its extremes are here, and
; reel_particles_mux's `millis` and arena_heap's power-of-two test as written.
; Not here (it fails the test, so no passing spec can hold it): a run-time zero divisor, or `@mod`'s negative one,
; which is a safety panic in Zig and a DivZero trap (a panic for `@mod`) in t27b
; (specs/tri/t27b/libm_plan.t27 lowers `@rem` as t27b's `%`).
Expand Down Expand Up @@ -114,3 +116,85 @@ test mod_of_unsigned_and_the_lcg {
i = i + 1;
}
}

fn ri_mod32(a: i32, b: i32) -> i32 {
return @mod(a, b);
}

fn ri_mod16(a: i16, b: i16) -> i16 {
return @mod(a, b);
}

fn ri_mod8(a: i8, b: i8) -> i8 {
return @mod(a, b);
}

fn ri_mod7(a: i7, b: i7) -> i7 {
return @mod(a, b);
}

// specs/t27f/reel_particles_mux.t27's `millis`, as written.
fn ri_millis(ms: i32) -> u32 {
return @as(u32, @intCast(@mod(ms, 1000)));
}

// specs/ml/arena_heap.t27's `is_pow2`, as written.
fn ri_is_pow2(n: i32) -> bool {
if (n < 1) {
return false;
}
var m : i32 = n;
while (m > 1) {
if (@mod(m, 2) != 0) {
return false;
}
m = @divTrunc(m, 2);
}
return true;
}

test a_narrow_signed_mod_takes_the_sign_of_the_divisor {
assert ri_mod32(7, 3) == 1;
assert ri_mod32(-7, 3) == 2;
assert ri_mod32(-6, 3) == 0;
assert ri_mod32(0, 7) == 0;
assert ri_mod32(-1, 2147483647) == 2147483646;
assert ri_mod32(-2147483648, 2147483647) == 2147483646;
assert ri_mod32(-2147483648, 3) == 1;
assert ri_mod32(2147483647, 2) == 1;
assert ri_mod32(2147483647, 2147483647) == 0;
assert ri_mod16(-32768, 7) == 6;
assert ri_mod16(-5, 32767) == 32762;
assert ri_mod16(32767, 10) == 7;
assert ri_mod8(-128, 3) == 1;
assert ri_mod8(-128, 127) == 126;
assert ri_mod8(127, 10) == 7;
assert ri_mod8(-1, 127) == 126;
assert ri_mod7(-64, 5) == 1;
assert ri_mod7(-1, 63) == 62;
assert ri_mod7(63, 2) == 1;
var a : i32 = -23;
while (a <= 23) {
const m : i32 = ri_mod32(a, 6);
assert m >= 0;
assert m < 6;
assert @rem(a - m, 6) == 0;
assert ri_mod32(a, 6) == @as(i32, @intCast(ri_mod(a, 6)));
a = a + 1;
}
}

test the_i32_mods_of_the_corpus_as_written {
assert ri_millis(1234) == 234;
assert ri_millis(-1) == 999;
assert ri_millis(-1000) == 0;
assert ri_millis(0) == 0;
assert ri_millis(2147483647) == 647;
assert ri_is_pow2(1);
assert ri_is_pow2(64);
assert ri_is_pow2(1073741824);
assert !ri_is_pow2(0);
assert !ri_is_pow2(-8);
assert !ri_is_pow2(96);
assert !ri_is_pow2(2147483647);
}
Loading
Loading