Skip to content

Inlining a function with an asm block accepting an array as input blows up the bytecode size #7150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ironcev opened this issue May 5, 2025 · 0 comments
Assignees
Labels
compiler: ir IRgen and sway-ir including optimization passes compiler General compiler. Should eventually become more specific as the issue is triaged team:compiler Compiler Team

Comments

@ironcev
Copy link
Member

ironcev commented May 5, 2025

Inlining a function with a single asm block accepting an array as input blows up the bytecode size.

A minimum repro is given below.

When the eq function is not inlined, the bytecode size is 1656 (without new encoding).
When the eq function is inlined, the bytecode size is 4952 (without new encoding).

script;

use std::crypto::message::Message;
use std::crypto::public_key::PublicKey;
use std::hash::sha256;

enum SignatureError {
    InvalidSignature: (),
}

pub struct Secp256k1 {
    bits: [u8; 64],
}

impl Secp256k1 {
    pub fn new() -> Self {
        Self {
            bits: [0u8; 64],
        }
    }

    pub fn recover(self, message: Message) -> Result<PublicKey, SignatureError> {
        Ok(PublicKey::from(message.try_into().unwrap()))
    }

    pub fn address(self, message: Message) -> Result<Address, SignatureError> {
        match self.recover(message) {
            Ok(pub_key) => Ok(Address::from(sha256(pub_key))),
            Err(e) => Err(e),
        }
    }
}

impl PartialEq for Secp256k1 {
    // #[inline(always)]
    #[inline(never)]
    fn eq(self, other: Self) -> bool {
        asm(result, r2: self.bits, r3: other.bits, r4: 64) {
            meq result r2 r3 r4;
            result: bool
        }
    }
}

fn main() {
    let message = Message::new();
    let secp256k1 = Secp256k1::new();
    let _ = secp256k1 == secp256k1;
    let _ = secp256k1.address(message);
    let _ = secp256k1.address(message);
}
@ironcev ironcev self-assigned this May 5, 2025
@ironcev ironcev added compiler General compiler. Should eventually become more specific as the issue is triaged compiler: ir IRgen and sway-ir including optimization passes team:compiler Compiler Team labels May 5, 2025
@ironcev ironcev changed the title Inlining a function with anasm block accepting an array as input blows up the bytecode size Inlining a function with an asm block accepting an array as input blows up the bytecode size May 5, 2025
ironcev added a commit that referenced this issue May 7, 2025
## Description

This PR optimizes the `std::crypto` module for bytecode size and gas
cost by using optimization techniques similar to those used in #7087,
#7092, and #7096.

The optimized public functions are listed in the below code snippet.
**The bytecode size of that code got reduced from 46104 bytes to 44288
bytes.**

We actually expect a bigger gain here. At one point in the optimization,
the bytecode size went down to 33896 bytes. Bouncing back to ~44600 is
related to the issue described in #7150. Once that issue is solved, we
expect even smaller bytcode sizes.

```sway
script;

use std::crypto::ed25519::*;
use std::crypto::point2d::*;
use std::crypto::public_key::*;
use std::crypto::scalar::*;
use std::crypto::secp256k1::*;
use std::crypto::secp256r1::*;
use std::crypto::signature::*;

use std::vm::evm::evm_address::EvmAddress;

fn main() {
    let ed25519 = std::crypto::ed25519::Ed25519::new();
    let _ = ed25519 == ed25519;

    let point2d = std::crypto::point2d::Point2D::new();
    let _ = point2d == point2d;

    let public_key = std::crypto::public_key::PublicKey::new();
    let _ = public_key == public_key;

    let _ = public_key.is_zero();

    let scalar = std::crypto::scalar::Scalar::new();
    let _ = scalar == scalar;

    let message = std::crypto::message::Message::new();

    let secp256k1 = std::crypto::secp256k1::Secp256k1::new();
    let _ = secp256k1 == secp256k1;
    let _ = secp256k1.address(message);
    let _ = secp256k1.evm_address(message);
    let _ = secp256k1.verify(public_key, message);
    let _ = secp256k1.verify_address(Address::zero(), message);
    let _ = secp256k1.verify_evm_address(EvmAddress::zero(), message);

    let secp256r1 = std::crypto::secp256r1::Secp256r1::new();
    let _ = secp256r1 == secp256r1;
    let _ = secp256r1.address(message);
    let _ = secp256r1.evm_address(message);
    let _ = secp256r1.verify(public_key, message);
    let _ = secp256r1.verify_address(Address::zero(), message);
    let _ = secp256r1.verify_evm_address(EvmAddress::zero(), message);
}
```

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] If my change requires substantial documentation changes, I have
[requested support from the DevRel
team](https://github.com/FuelLabs/devrel-requests/issues/new/choose)
- [ ] I have added tests that prove my fix is effective or that my
feature works.
- [ ] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler: ir IRgen and sway-ir including optimization passes compiler General compiler. Should eventually become more specific as the issue is triaged team:compiler Compiler Team
Projects
None yet
Development

No branches or pull requests

1 participant