Skip to content
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

Cross-Inline-Assembly block functions. #1319

Closed
1 task
chriseth opened this issue Nov 2, 2016 · 12 comments
Closed
1 task

Cross-Inline-Assembly block functions. #1319

chriseth opened this issue Nov 2, 2016 · 12 comments
Labels
closed due inactivity The issue/PR was automatically closed due to inactivity. stale The issue/PR was marked as stale because it has been open for too long.

Comments

@chriseth
Copy link
Contributor

chriseth commented Nov 2, 2016

TODO:

  • make inline assembly functions globally available

Most of the ideas below have been implemented. What we are missing is functions that can be used across multiple inline-assembly blocks. In the code generator, this is supported via "named labels".

It is not really supported in the assembly parser, though, but we could always prefix all known inline assembly functions that parsed correctly, perhaps even without body?


Solidity (inline) assembly should support macros in the following way:

define <macroname>(<param1>, ..., <param_n>) -> [(<retparam1>, ..., <retparam_m>)] { <assembly code> }

The defined macro will be visible and usable in inline assembly blocks in the whole code unit (this includes the current contract, base contracts and internal functions of libraries that are called, but construction-time code is disconnected from runtime code).

Remark: currently labels are only visible in the current inline assembly block, so we should aligns those two behaviours.

It is an error to define two macros of the same name in the same code unit.

If the return parameters are not given, the compiler will try to infer the stack difference caused by the macro. If it is not able to do that or if there are two code paths with different stack differences, this is an error.

Inside the assembly code, both the parameters and the return parameters are available as variables and they are assigned contiguous stack slots (left: deep, right: top) where the first parameter and the first return parameter refer to the same slot. If there are more parameters than return parameters, the surplus stack items are popped automatically.

A macro can be used in the same way as an opcode can be used with the same restriction for usage in functional style notation.

Note that macros are more similar to custom opcodes - they are not a replacement system in that their arguments are evaluated before they are "called" just like with evm opcodes and not inserted literally into the macro's body.

If a macro is called, it is usually just replaced by its body, but it is also possible to use macros recursively. In this case, and also if it makes sense from an optimization perspective, the macro body is moved out-of-place and it is called like a function.

The compiler will also define several globally available macros. Those macros will always start with $ and thus, such macro names can be considered reserved.

@chriseth
Copy link
Contributor Author

chriseth commented Nov 4, 2016

The solution regarding visibility of labels and macros could be the following:
All label and macro definitions in inline assembly are automatically prefixed with the current contract and function name during the translation to actual assembly. Label and macro references are first looked up at the local "scope" (by prefixing it with contract and function name) and then more and more general scopes until a match is found.

@axic
Copy link
Member

axic commented Nov 4, 2016

Somewhat related: #474

@chriseth
Copy link
Contributor Author

chriseth commented Nov 5, 2016

Some examples:

// 64 bit unsigned multiplication with overflow detection
define mul_u64(x, y) -> z
{
  z := mul(and(x, 0xffffffffffffffff), and(y, 0xffffffffffffffff))
  jumpi(ErrorTag, gt(z, 0xffffffffffffffff))
}

@axic
Copy link
Member

axic commented Nov 6, 2016

The macros I'd like to see (copied from #474):

  • shifts
  • memory allocation
  • memory copy
// allocates a memory block
define alloc(size) -> offset
{
  // current end of memory
  offset := mload(0x40)
  // new "end of memory" including padding
  mstore(0x40, add(offset, and(add(size, 0x1f), bnot(0x1f))))
}

// allocates memory for a bytes/string type
define allocbytes(size) -> offset
{
  offset := $alloc(add(size, 32))
  mstore(offset, size)
}

@chriseth
Copy link
Contributor Author

chriseth commented Nov 7, 2016

Your example usage shows that we have to refine our definition of macros with regards to their return values. As currently specified, offset and size point to the same stack slot, but most people will probably not read that part. So it is probably better to shift the arguments and return values around at the end of the macro. This makes short macros much less efficient, unless the optimizer can do its job there.

What about adding the following part to the specification:

If return parameters are provided, the macro is expanded with the following prefix and suffix:

prefix: allocate a new stack slot for each return parameter, setting them to zero
suffix: shift the stack around removing the argument variables

If no return parameters are provided (in the sense that they are named variables, the macro can still return values to the stack), there is no prefix and suffix.

@chriseth chriseth added in progress and removed soon labels Feb 10, 2017
@axic axic changed the title Macros / functions for inline assembly Assembly: support macros / functions for inline assembly Jul 9, 2017
@chriseth
Copy link
Contributor Author

Changed the initial description to word it more towards making inline assembly functions accessible across inline assembly blocks.

@chriseth chriseth changed the title Assembly: support macros / functions for inline assembly Cross-Inline-Assembly block functions. Mar 15, 2018
@chriseth
Copy link
Contributor Author

Actually I think the suggestion in the comment above could work rather well. We have to introduce another lookup method for function identifiers (and not just variable identifiers). Furthermore, inline assembly functions have to be registered at the compiler context, and not just the inline assembly AST node. Compiler-generated functions have a different prefix and thus cannot clash (but you can still use them if you want).

@axic axic added the feature label Mar 26, 2018
@axic axic removed the in progress label Aug 2, 2018
@axic
Copy link
Member

axic commented Aug 28, 2019

If we are talking about macros, perhaps it would make sense introducing a symbol differentiating them from opcodes. One example is the exclamation mark from Rust: assert!() is a macro, while assert() is a function.

@MicahZoltu
Copy link
Contributor

My general feeling is that epsilon engineering effort should go into improving inline assembly. Instead, Solidity should be looking at what people are building with inline assembly and figuring out higher level constructs that facilitate doing that in Solidity, so they don't need to drop to inline assembly.

Anytime someone drops to inline assembly suggests a failure of the language, or at least an area where the language can improve. Also, inline assembly really isn't appropriate for smart contracts, which are supposed to be public, easily auditable code blocks and assembly makes the code harder to read/audit.

@chriseth
Copy link
Contributor Author

chriseth commented Sep 9, 2019

"public, easily auditable code blocks" - exactly my words! I'm just drawing a different conclusion: Complicated code that is hidden in the compiler is worse than structured assembly code that can be easily found in a library that is linked together with the project.

But it always depends on the use-case and what you do with inline assembly. What people do with it should be an inspiration for language features.

@github-actions
Copy link

This issue has been marked as stale due to inactivity for the last 90 days.
It will be automatically closed in 7 days.

@github-actions github-actions bot added the stale The issue/PR was marked as stale because it has been open for too long. label Feb 12, 2023
@github-actions
Copy link

Hi everyone! This issue has been automatically closed due to inactivity.
If you think this issue is still relevant in the latest Solidity version and you have something to contribute, feel free to reopen.
However, unless the issue is a concrete proposal that can be implemented, we recommend starting a language discussion on the forum instead.

@github-actions github-actions bot added the closed due inactivity The issue/PR was automatically closed due to inactivity. label Feb 20, 2023
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Feb 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed due inactivity The issue/PR was automatically closed due to inactivity. stale The issue/PR was marked as stale because it has been open for too long.
Projects
None yet
Development

No branches or pull requests

4 participants