huff-language / huffmate

A library of modern, hyper-optimized, and extensible Huff contracts with extensive testing and documentation built by Huff maintainers.
https://github.com/pentagonxyz/huffmate
MIT License
437 stars 55 forks source link

Fix ternary tests and optimize TERNARY macro #97

Closed d1ll0n closed 1 year ago

d1ll0n commented 1 year ago

Old TERNARY macro:

// Input Stack: [condition, x, y]

// If the condition is false, jump and pop
iszero __Utils_Misc__ternaryNoSwap jumpi
    swap1 // [y, x]
__Utils_Misc__ternaryNoSwap:
   // condition ? [y, x] : [x, y]
    pop // condition ? [x] : [y]

When the condition is true, the operations are: iszero push jumpi swap jumpdest pop = 23 gas When it's false: iszero push jumpi jumpdest pop = 20 gas

If we use branchless logic we can get this down to 17 for both cases:


// Input Stack: [condition, x, y]
swap1           // [x, condition, y]
dup3            // [y, x, condition, y]
xor mul xor     // [((x ^ y) * condition) ^ y]
// Output Stack: [condition ? x : y]

swap1 dup3 xor mul xor = 17 gas

I don't think it's possible to do the same optimization for the inverse macro NOT_TERNARY because you would either need to swap x and y in the formula, adding an additional swap operation, or use iszero. Since that macro is already 3 gas less than the TERNARY macro, adding another operation to the branchless version makes it equivalent for one case and more expensive for the other.

For reference, there are two formulae you can use:

// condition ? x : y

(x - y) * condition + y
// Required Stack: [x, y, condition, y]
// sub mul add

((x ^ y) * condition) ^ y
// Required Stack: [x, y, condition, y] OR [y, x, condition, y]
// xor mul xor

And for the inverse

// condition ? y : x

(y - x) * condition + x
// Required Stack: [y, x, condition, x]
// sub mul add

((x ^ y) * condition) ^ x
// Required Stack: [x, y, condition, x] OR [y, x, condition, x]
// xor mul xor