From 8d384119f17e98c0bdc7fdb6d5c6c09ca4106f4d Mon Sep 17 00:00:00 2001 From: "N.E" Date: Sun, 19 Jun 2022 15:05:44 -0400 Subject: [PATCH 1/2] Statically assert that delays are less than 25,769,803,784 cycles The maximum delay that the Delayer efficiently supports is 25,769,803,784 cycles. Beyond this, previously the implementation would support slightly longer delays (about 0.3% longer) but use many more instructions to do so, and for even longer delays would overflow silently. This commit adds a static check that the requested delay is less than 25,769,803,784 cycles. If the delay is longer than that, the compilation panics with an error message stating that the delay is too long. --- src/delay_cycles.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/delay_cycles.rs b/src/delay_cycles.rs index 9aac747..8b7ea83 100644 --- a/src/delay_cycles.rs +++ b/src/delay_cycles.rs @@ -72,6 +72,19 @@ struct Selection { remainder: u64, } +// Compute the intended number of cycles to delay, and panic if it is greater than +// the maximum supported amount of cycles +const fn compute_total_cycles(cycles: u64, mul: u64, div: u64) -> u64 { + const MAX_SUPPORTED_CYCLES: u64 = 25_769_803_784; + + // Multiply first to avoid precision loss, and expand to u128 to avoid overflow + let result: u128 = (cycles as u128) * (mul as u128) / (div as u128); + if result > (MAX_SUPPORTED_CYCLES as u128) { + panic!("Error: Tried to delay for too many cycles. The maximum supported delay is 25_769_803_784 cycles"); + } + return result as u64; +} + const fn cycles(counter_mask: u64, cycles_per_run: u64, cycles_per_iter: u64) -> Cycles { Cycles { counter_mask, @@ -103,11 +116,7 @@ const fn select(info: Cycles, cycles: u64, above: u64) -> Selection { } impl Delayer { - // Multiply first to avoid precision loss. - // With a u64 there is no overflow when MUL is lower than: - // (2^64-1)/25_769_803_784 == 715_827_882. - // Since MUL is usually CPU_FREQUENCY_HZ, this allows up to 715.83 MHz. - const TOTAL_CYCLES: u64 = CYCLES * MUL / DIV; + const TOTAL_CYCLES: u64 = compute_total_cycles(CYCLES, MUL, DIV); // With `feature(generic_const_exprs) it becomes possible to construct a static assertion. //const _: [(); 0 - ((Self::TOTAL_CYCLES > 25_769_803_784) as usize)] = []; From 31dbd31b6a250d4ce52404b315728e88cd48cd11 Mon Sep 17 00:00:00 2001 From: "N.E" Date: Wed, 29 Jun 2022 09:18:59 +0300 Subject: [PATCH 2/2] Minor cleanup to compile-time assertion - Remove obsolete comment. - Remove compute_total_cycles function which is only called once, and instead use an expression block. - Use assert! instead of if and panic!. - Use <= instead of < when checking number of cycles. --- src/delay_cycles.rs | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/delay_cycles.rs b/src/delay_cycles.rs index 8b7ea83..d2fd020 100644 --- a/src/delay_cycles.rs +++ b/src/delay_cycles.rs @@ -72,19 +72,6 @@ struct Selection { remainder: u64, } -// Compute the intended number of cycles to delay, and panic if it is greater than -// the maximum supported amount of cycles -const fn compute_total_cycles(cycles: u64, mul: u64, div: u64) -> u64 { - const MAX_SUPPORTED_CYCLES: u64 = 25_769_803_784; - - // Multiply first to avoid precision loss, and expand to u128 to avoid overflow - let result: u128 = (cycles as u128) * (mul as u128) / (div as u128); - if result > (MAX_SUPPORTED_CYCLES as u128) { - panic!("Error: Tried to delay for too many cycles. The maximum supported delay is 25_769_803_784 cycles"); - } - return result as u64; -} - const fn cycles(counter_mask: u64, cycles_per_run: u64, cycles_per_iter: u64) -> Cycles { Cycles { counter_mask, @@ -116,10 +103,15 @@ const fn select(info: Cycles, cycles: u64, above: u64) -> Selection { } impl Delayer { - const TOTAL_CYCLES: u64 = compute_total_cycles(CYCLES, MUL, DIV); - - // With `feature(generic_const_exprs) it becomes possible to construct a static assertion. - //const _: [(); 0 - ((Self::TOTAL_CYCLES > 25_769_803_784) as usize)] = []; + // Compute the intended number of cycles to delay, and panic if it is greater than + // the maximum supported amount of cycles + const TOTAL_CYCLES: u64 = { + const MAX_SUPPORTED_CYCLES: u64 = 25_769_803_784; + // Multiply first to avoid precision loss, and expand to u128 to avoid overflow + let result: u128 = (CYCLES as u128) * (MUL as u128) / (DIV as u128); + assert!(result <= (MAX_SUPPORTED_CYCLES as u128), "Error: Tried to delay for too many cycles. The maximum supported delay is 25_769_803_784 cycles"); + result as u64 + }; // counter mask, cycles per run, cycles per iteration. | cost + worst case remainder cost const U32_INFO: Cycles = cycles(0xFFFF_FFFF, 9, 6); // 8 + 3