Skip to content
Draft
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
5 changes: 5 additions & 0 deletions docs/SDK2.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,11 @@ without floating-point SBF runtime helpers. Sizes above 10 MiB, a uint64 overflo
in `(128+size)*rate`, and negative/nonfinite thresholds return `ErrRent` (3020).
Both signed zeros are accepted. The integer-overflow rejection is an intentional
bounded policy; it does not reproduce Rust release-mode wrapping of that product.
With a threshold of exactly 2.0 (Fogo, and Solana before SIMD-0194) or 1.0
(after it) and `(128+size)*rate` below 2^53, the conversion and product are
exact, so the result is twice or once that product and the rounding emulation
is skipped: 480 to 760 CU less per call on SBF, and 2 to 8 CU more on other
thresholds ([measurements](../results/sdk/2026-10-19-rent-fast-path/README.md)).

`solana.Rent(context, output)` exposes the raw runtime values as exactly 17 bytes:
little-endian uint64 rate, little-endian uint64 threshold bits, and one burn byte.
Expand Down
27 changes: 27 additions & 0 deletions examples/escrow/.gosvm/sdk/sdk/system/rent.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,40 @@ func product(x, y uint64) (uint64, uint64) {
return x1*y1 + w2 + w1>>32, x * y
}

// IEEE-754 bits of the exemption thresholds with an exact fast path: 2.0
// (Fogo, and Solana before SIMD-0194) and 1.0 (Solana after it).
const (
thresholdTwo uint64 = 0x4000000000000000
thresholdOne uint64 = 0x3ff0000000000000
)

// MinimumBalanceFor matches the pinned solana-rent 3.0.0 minimum_balance's
// uint64 -> f64 conversion, f64 multiplication, and saturating uint64 cast.
// Integer bit operations reproduce the two round-to-nearest-even stages without
// floating-point runtime helpers on SBF. Unlike Rust's release-mode integer
// wrap, overflow in (128+size)*rate is an explicit error. Sizes over 10 MiB and
// negative/nonfinite thresholds are also rejected; signed zero is accepted.
//
// With a threshold of 2.0 or 1.0 and n = (128+size)*rate below 2^53, the
// conversion and the product are exact, so the result is 2n or n and the
// rounding emulation is skipped.
func MinimumBalanceFor(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
if n := (128 + size) * rate; n < 1<<53 {
if thresholdBits == thresholdTwo {
return 2 * n, 0
}
if thresholdBits == thresholdOne {
return n, 0
}
}
return minimumBalanceExact(rate, thresholdBits, size)
}

// minimumBalanceExact is MinimumBalanceFor without the fast path.
func minimumBalanceExact(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
Expand Down
27 changes: 27 additions & 0 deletions examples/fee-vault/.gosvm/sdk/sdk/system/rent.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,40 @@ func product(x, y uint64) (uint64, uint64) {
return x1*y1 + w2 + w1>>32, x * y
}

// IEEE-754 bits of the exemption thresholds with an exact fast path: 2.0
// (Fogo, and Solana before SIMD-0194) and 1.0 (Solana after it).
const (
thresholdTwo uint64 = 0x4000000000000000
thresholdOne uint64 = 0x3ff0000000000000
)

// MinimumBalanceFor matches the pinned solana-rent 3.0.0 minimum_balance's
// uint64 -> f64 conversion, f64 multiplication, and saturating uint64 cast.
// Integer bit operations reproduce the two round-to-nearest-even stages without
// floating-point runtime helpers on SBF. Unlike Rust's release-mode integer
// wrap, overflow in (128+size)*rate is an explicit error. Sizes over 10 MiB and
// negative/nonfinite thresholds are also rejected; signed zero is accepted.
//
// With a threshold of 2.0 or 1.0 and n = (128+size)*rate below 2^53, the
// conversion and the product are exact, so the result is 2n or n and the
// rounding emulation is skipped.
func MinimumBalanceFor(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
if n := (128 + size) * rate; n < 1<<53 {
if thresholdBits == thresholdTwo {
return 2 * n, 0
}
if thresholdBits == thresholdOne {
return n, 0
}
}
return minimumBalanceExact(rate, thresholdBits, size)
}

// minimumBalanceExact is MinimumBalanceFor without the fast path.
func minimumBalanceExact(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
Expand Down
27 changes: 27 additions & 0 deletions examples/find-address/.gosvm/sdk/sdk/system/rent.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,40 @@ func product(x, y uint64) (uint64, uint64) {
return x1*y1 + w2 + w1>>32, x * y
}

// IEEE-754 bits of the exemption thresholds with an exact fast path: 2.0
// (Fogo, and Solana before SIMD-0194) and 1.0 (Solana after it).
const (
thresholdTwo uint64 = 0x4000000000000000
thresholdOne uint64 = 0x3ff0000000000000
)

// MinimumBalanceFor matches the pinned solana-rent 3.0.0 minimum_balance's
// uint64 -> f64 conversion, f64 multiplication, and saturating uint64 cast.
// Integer bit operations reproduce the two round-to-nearest-even stages without
// floating-point runtime helpers on SBF. Unlike Rust's release-mode integer
// wrap, overflow in (128+size)*rate is an explicit error. Sizes over 10 MiB and
// negative/nonfinite thresholds are also rejected; signed zero is accepted.
//
// With a threshold of 2.0 or 1.0 and n = (128+size)*rate below 2^53, the
// conversion and the product are exact, so the result is 2n or n and the
// rounding emulation is skipped.
func MinimumBalanceFor(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
if n := (128 + size) * rate; n < 1<<53 {
if thresholdBits == thresholdTwo {
return 2 * n, 0
}
if thresholdBits == thresholdOne {
return n, 0
}
}
return minimumBalanceExact(rate, thresholdBits, size)
}

// minimumBalanceExact is MinimumBalanceFor without the fast path.
func minimumBalanceExact(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
Expand Down
27 changes: 27 additions & 0 deletions examples/full-swap/.gosvm/sdk/sdk/system/rent.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,40 @@ func product(x, y uint64) (uint64, uint64) {
return x1*y1 + w2 + w1>>32, x * y
}

// IEEE-754 bits of the exemption thresholds with an exact fast path: 2.0
// (Fogo, and Solana before SIMD-0194) and 1.0 (Solana after it).
const (
thresholdTwo uint64 = 0x4000000000000000
thresholdOne uint64 = 0x3ff0000000000000
)

// MinimumBalanceFor matches the pinned solana-rent 3.0.0 minimum_balance's
// uint64 -> f64 conversion, f64 multiplication, and saturating uint64 cast.
// Integer bit operations reproduce the two round-to-nearest-even stages without
// floating-point runtime helpers on SBF. Unlike Rust's release-mode integer
// wrap, overflow in (128+size)*rate is an explicit error. Sizes over 10 MiB and
// negative/nonfinite thresholds are also rejected; signed zero is accepted.
//
// With a threshold of 2.0 or 1.0 and n = (128+size)*rate below 2^53, the
// conversion and the product are exact, so the result is 2n or n and the
// rounding emulation is skipped.
func MinimumBalanceFor(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
if n := (128 + size) * rate; n < 1<<53 {
if thresholdBits == thresholdTwo {
return 2 * n, 0
}
if thresholdBits == thresholdOne {
return n, 0
}
}
return minimumBalanceExact(rate, thresholdBits, size)
}

// minimumBalanceExact is MinimumBalanceFor without the fast path.
func minimumBalanceExact(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
Expand Down
27 changes: 27 additions & 0 deletions examples/logging/.gosvm/sdk/sdk/system/rent.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,40 @@ func product(x, y uint64) (uint64, uint64) {
return x1*y1 + w2 + w1>>32, x * y
}

// IEEE-754 bits of the exemption thresholds with an exact fast path: 2.0
// (Fogo, and Solana before SIMD-0194) and 1.0 (Solana after it).
const (
thresholdTwo uint64 = 0x4000000000000000
thresholdOne uint64 = 0x3ff0000000000000
)

// MinimumBalanceFor matches the pinned solana-rent 3.0.0 minimum_balance's
// uint64 -> f64 conversion, f64 multiplication, and saturating uint64 cast.
// Integer bit operations reproduce the two round-to-nearest-even stages without
// floating-point runtime helpers on SBF. Unlike Rust's release-mode integer
// wrap, overflow in (128+size)*rate is an explicit error. Sizes over 10 MiB and
// negative/nonfinite thresholds are also rejected; signed zero is accepted.
//
// With a threshold of 2.0 or 1.0 and n = (128+size)*rate below 2^53, the
// conversion and the product are exact, so the result is 2n or n and the
// rounding emulation is skipped.
func MinimumBalanceFor(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
if n := (128 + size) * rate; n < 1<<53 {
if thresholdBits == thresholdTwo {
return 2 * n, 0
}
if thresholdBits == thresholdOne {
return n, 0
}
}
return minimumBalanceExact(rate, thresholdBits, size)
}

// minimumBalanceExact is MinimumBalanceFor without the fast path.
func minimumBalanceExact(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
Expand Down
27 changes: 27 additions & 0 deletions examples/tutorial/.gosvm/sdk/sdk/system/rent.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,40 @@ func product(x, y uint64) (uint64, uint64) {
return x1*y1 + w2 + w1>>32, x * y
}

// IEEE-754 bits of the exemption thresholds with an exact fast path: 2.0
// (Fogo, and Solana before SIMD-0194) and 1.0 (Solana after it).
const (
thresholdTwo uint64 = 0x4000000000000000
thresholdOne uint64 = 0x3ff0000000000000
)

// MinimumBalanceFor matches the pinned solana-rent 3.0.0 minimum_balance's
// uint64 -> f64 conversion, f64 multiplication, and saturating uint64 cast.
// Integer bit operations reproduce the two round-to-nearest-even stages without
// floating-point runtime helpers on SBF. Unlike Rust's release-mode integer
// wrap, overflow in (128+size)*rate is an explicit error. Sizes over 10 MiB and
// negative/nonfinite thresholds are also rejected; signed zero is accepted.
//
// With a threshold of 2.0 or 1.0 and n = (128+size)*rate below 2^53, the
// conversion and the product are exact, so the result is 2n or n and the
// rounding emulation is skipped.
func MinimumBalanceFor(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
if n := (128 + size) * rate; n < 1<<53 {
if thresholdBits == thresholdTwo {
return 2 * n, 0
}
if thresholdBits == thresholdOne {
return n, 0
}
}
return minimumBalanceExact(rate, thresholdBits, size)
}

// minimumBalanceExact is MinimumBalanceFor without the fast path.
func minimumBalanceExact(rate, thresholdBits, size uint64) (uint64, uint64) {
if size > MaxDataLen || rate > ^uint64(0)/(128+size) {
return 0, ErrRent
}
Expand Down
6 changes: 6 additions & 0 deletions internal/compiler/alloc_sbf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,12 @@ func runAllocProgram(t *testing.T, llvm, runner, profile, name string, source []
if err := os.WriteFile(filepath.Join(dir, "program.go"), source, 0644); err != nil {
t.Fatal(err)
}
return runAllocDir(t, llvm, runner, profile, name, dir, heap, cases)
}

// runAllocDir is runAllocProgram for a program already written into dir.
func runAllocDir(t *testing.T, llvm, runner, profile, name, dir string, heap int, cases []allocCase) map[string]any {
t.Helper()
elf := filepath.Join(dir, "program.so")
if err := BuildWithOptions(dir, elf, llvm, profile, BuildOptions{HeapBytes: heap}); err != nil {
t.Fatal(err)
Expand Down
Loading
Loading