solidity
solidity copied to clipboard
Compiling 16 files with 0.7.6 CompilerError: Stack too deep when compiling inline assembly: Variable headStart is 2 slot(s) too deep inside the stack. Error HH600: Compilation failed
This works fine in Remix with/wo optimization using 0.7.6
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9.0;
pragma abicoder v2;
contract PurchaseStruct {
struct Purchase {
address purchaser; // 20 Byte
uint48 timestamp; // 6 Byte
// uint32 spare ; // 4 Byte
bool wasFinalized; // 1 Byte
bool reverted; // 1 Byte
uint256 amount;
uint256 costAmount;
uint256 amountRedeemed;
}
Purchase[] public purchases; // Array with all purchase structs
function addPurchase() public {
purchases.push(Purchase({
amount: 1234567890, // _amount,
purchaser: msg.sender,
costAmount: 1234, // value,
timestamp: uint48(block.timestamp),
amountRedeemed: 0,
wasFinalized: false, // isTokenSwapAtomic, /* If Atomic Swap */
reverted: false
}));
}
function getPurchaseStruct(uint256 purchase_id) external view returns (Purchase memory purchase) {
return purchases[purchase_id];
}
}
However within my larger hardhat based project (just one contract with a few OpenZeppelin imports), as soon as I add pragma abicoder v2; I get this error message :
Compiling 16 files with 0.7.6
CompilerError: Stack too deep when compiling inline assembly: Variable headStart is 2 slot(s) too deep inside the stack.
Error HH600: Compilation failed
hardhat.config.ts
{
version: "0.7.6",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
Unfortunately, this is a known issue, and does not have a good fix. (We are working on a solution for upcoming solidity versions, but until then, sorry)
Here are two suggestions that are not perfect, but might work
- Consider reducing the number of struct members.
- Consider disabling the Yul optimizer:
{
version: "0.7.6",
settings: {
optimizer: {
enabled: true,
runs: 200,
details: {
yul: false
}
},
},
},
If you do the second option and it works, could you please follow up?
@hrkrshnn
Unfortunately yul: false does not make any difference.
It's kind of disappointing as I don't even have a large or complex contract. It's actually the code as above with some extra variables and functions "beside" that, so not even nested ... so I find it strange that the stack does not have 0 entries and can easily cope with it ...
@SvenMeyer Could you post a minimal example with the stack too deep error? Perhaps we could help with a specific suggestion to fix it.
BTW, the function getPurchaseStruct is redundant here, since the compiler will automatically generate a getter for public state variables.
BTW, the function
getPurchaseStructis redundant here, since the compiler will automatically generate a getter for public state variables.
Though there's a gotcha to watch out for if the actual struct is not the same as in the code above: a getter would skip any array fields within the struct.
@hrkrshnn even without the getPurchaseStruct() I get the compiler error as soon as I add pragma abicoder v2;
I tried to come up with minimal version, that's the code above which I initally posted ... and that works.
Hard to say what needs to be removed to just break it. The struct does not look too complicated (with 7 entries , 4 x 256 Bit words) to me as well and can't be reduced further.
I invited you to my repo, just in case you want to have a look and may spot something at first sight ...
I assume solc 0.8.x would not make a difference either ?
I assume solc 0.8.x would not make a difference either ?
@SvenMeyer You can try running with the latest release / develop. There were some minor changes in stack management that might help.
@hrkrshnn
Compiling 21 files with 0.8.4
CompilerError: Stack too deep when compiling inline assembly: Variable headStart is 2 slot(s) too deep inside the stack.
Error HH600: Compilation failed
Compiling 21 files with 0.8.6
CompilerError: Stack too deep when compiling inline assembly: Variable headStart is 2 slot(s) too deep inside the stack.
Error HH600: Compilation failed
:-(
@hrkrshnn
looks like with solc 0.8.4 and 0.8.6 I even get the error with pragma abicoder v2; commented out ... what ?? !!
https://github.com/polkastarter/fixed-swap-offchain-whitelist/tree/solc_0.8
@SvenMeyer In 0.8.0, abi encoder v2 is enabled by default, please check the release notes.
@lepidotteri OVM is a project independent of solidity. AFAIK, OVM currently doesn't support 0.8.*. Please ask them directly about the status.
Here are two suggestions that are not perfect, but might work
- Consider reducing the number of struct members.
- Consider disabling the Yul optimizer: ... If you do the second option and it works, could you please follow up?
Howdy all. Just wanted to let you know I did Option 2 and it fixed my problem.
I was adding the diamond proxy contracts by @mudgen to my project (which is being compiled with 0.8.5). Even though his project was not getting the error, I was consistently getting the following in mine after adding his contracts:
CompilerError: Stack too deep when compiling inline assembly: Variable headStart is 1 slot(s) too deep inside the stack.
The only difference was that @mudgen's hardhat.config.js had super simple config:
module.exports = {
solidity: '0.8.6'
}
I personally had more going on:
solidity: {
version: "0.8.5",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
I had previously discovered that setting optimizer to false in my config would get rid of the error, but I don't want to go live like that so it wasn't a solution. Then I found @hrkrshnn's post about turning off YUL optimizer
solidity: {
version: "0.8.5",
settings: {
optimizer: {
enabled: true,
runs: 200,
details: {
yul: false
}
}
}
}
My only question now is what the effect of disabling the YUL optimizer will be.
My only question now is what the effect of disabling the YUL optimizer will be.
@cliffhall The ABI encoder v2 code for your contract would not be optimized.
Had the same problemm with hardhat and larger number of structs. Putting
yul: false
helped.
I am getting the same issue with 0.8.9 version of Solidity while trying to migrate from 0.7.x version. It doesn't even tell which file produce the issue. Is there way to check? None of suggested compiler settings helped with the problem.
Unfortunately not, because at the stage where the problem happens the connection between your original source and the part that is being processed is pretty tenuous. The code is already after conversion to Yul, optimization (including inlining) and is being translated from Yul to EVM bytecode. The Yul source being compiled is just a big dump of code from your contract and any other contracts/functions it calls internally. The information about which parts of your original source this Yul code maps to is either unavailable or unreliable. We'd like to improve error reporting for that case (#12449) but there isn't much that can be done without guessing, and in the end focusing more on features that will make these errors happen less seems more productive.
getting similar error on 0.8.13
@chee-chyuan Can you try setting viaIR to true?
@chee-chyuan Can you try setting
viaIRto true?
thanks. compiling takes abit more time but that works
@chee-chyuan Can you try setting
viaIRto true?thanks. compiling takes abit more time but that works
also got in touch with etherscan & remix. hopefully they can support that soon
I'm having this problem on Solidity v0.8.10 without using abiencoder v2.
Can you try setting viaIR to true?
How and where does one do this?
I'm having this problem on Solidity v0.8.10 without using abiencoder v2.
Can you try setting viaIR to true?
How and where does one do this?
im not sure if its available in v0.8.10. got to ask to team
Ok... Regardless of version, how does one do that?
Simply add --via-ir option to compiler's command line. Or, when using Standard JSON it's a boolean settings.viaIR setting, see Compiler Input and Output JSON Description. If you're not using the compiler directly, your framework probably lets you specify the settings part from Standard JSON in its config file.
im not sure if its available in v0.8.10. got to ask to team
It's been available at least since 0.7.x, maybe even 0.6.x (you can check if your version supports it in solc --help). Just note that the CLI option was called --experimental-via-ir until 0.8.12. We only consider it stable and ready for general use starting with 0.8.13. Using it in production with earlier binaries is not recommended. In standard JSON the name did not change.
Thanks for the info and resources! Very helpful. Unfortunately nothing helped. I've tried disabling the Yul optimizer, disabling the optimizer entirely, enabling viaIR, disabling peephole, enabling Yul stack allocation.
In my case I'm getting only 1 slot too deep error.
On 0.8.13? Can you post a new snippet that reproduces this?
Turns out it was because my contract constructor had too many input arguments:
contract Breaks {
constructor(string memory name_, string memory symbol_,
address _0,
address _1,
address _2,
address _3,
address _4,
address _5,
address _6,
address _7,
address _8,
address _9
) {}
}
Removing address _9 compiles fine.
Although oddly, if I add any more arguments to the constructor, the error still reports Stack too deep when compiling inline assembly: Variable headStart is 1 slot(s) too deep inside the stack., whereas I would have expected the error to deepen in slots.
Thanks. So I just checked that with --via-ir and it compiles just fine, even with more arguments. So switching to 0.8.13 and using the IR codegen should solve the problem for you.
I have the same issue when my struct has too many variables. How can I turn this --via-ir on? I am using truffle. When I add this to truffle-config.js I have still the same issue. Maybe I add it in the wrong way?
Here is my current config with this key
`require('babel-register'); require('babel-polyfill');
module.exports = { networks: { development: { host: "127.0.0.1", port: 7545, network_id: "*" // Match any network id }, }, contracts_directory: './src/contracts/', contracts_build_directory: './src/abis/', compilers: { solc: { version : "^0.8.13", optimizer: { enabled: true, runs: 200 }, evmVersion: "petersburg" } }, viaIR : true }`
Thank you
@cameel
- How would I set
--via-irin a hardhat environment ? - What is
--via-iractually doing ?
--via-iris a CLI option. Hardhat and Truffle use Standard JSON, so the setting issettings.viaIRas I mentioned above.- Compiles to Yul first and then Yul to EVM assembly. See details in Solidity 0.8.13 Release Announcement and Solidity IR-based Codegen Changes.
@cameel Thanks for that !
I added viaIR:true and was happy to see that contract size was reduced from ~24.132 to 23.555 .. then realized I had still set it to 0.8.12, switched it to 0.8.13, and it went up to 24.19 :-(
Any explanantion for that ?
solidity: {
compilers: [
{
version: "0.7.6",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
{
version: "0.8.13",
settings: {
metadata: {
// Not including the metadata hash
// https://github.com/paulrberg/solidity-template/issues/31
bytecodeHash: "none",
},
optimizer: {
enabled: true,
runs: 800,
},
viaIR : true,
},
},
],
},
Actually, version: "0.8.13" & viaIR: true gives me the largest code size :-(
| solc | viaIR | code size |
|---|---|---|
| 0.8.12 | false | 24.132 |
| 0.8.12 | true | 23.555 |
| 0.8.13 | false | 24.150 |
| 0.8.13 | true | 24.190 |
Still using version: "0.8.13" & viaIR: true , optimizer.runs makes a big difference (and may should):
| runs | code size |
|---|---|
| 100 | 22.912 |
| 200 / not specified | 23.299 |
| 800 | 24.190 |
Is it actually (still) advised to specify optimizer.runs ?
optimizer.runs is the way you tell the compiler what you care more about: the deployment cost or the runtime cost. The value is the number of times you expect it to run over its lifetime and the compiler makes the trade-off accordingly. The lower it is, the smaller your bytecode will be, even at the cost of making the execution more expensive. If you're going to use your contract only once, the deployment cost is just as important as runtime cost. If you're going to call it thousands of times, the deployment cost will be dwarfed by the runtime cost.
Any explanantion for that ?
We do lots of changes that affect the cost one way or another. Optimizations lower it, security-related changes often increase it so small differences like these are not uncommon.
For example we recently merged #12647, which slightly increases bytecode size in most cases.
Even if a change is an optimization that lowers the cost in most cases, there are pretty much always some corner cases where the cost goes up.
For viaIR you're likely going to see a significant improvement in one of the upcoming versions. We're currently working on making inlining more aggressive (#12731), which looks like it should reduce the bytecode size by 5-10% in many real-life projects. Maybe even more if we manage to completely remove function size limit.
Enabling yul worked for me with next settings:
solidity: {
version: "0.8.11",
settings: {
optimizer: {
runs: 200,
enabled: true,
details: {
yul: true
}
},
},
},
@hrkrshnn I was getting
CompilerError: Stack too deep when compiling inline assembly: Variable value3 is 6 slot(s) too deep inside the stack.
Error HH600: Compilation failed
This error this was fixed by turning off yul optimiser
@Alfa11917676 Was it with viaIR: true? What does you code look like? Can you post a small snippet that reproduces this?
@cameel my code was a on-chain metadata generator with a lot of traits coming into actions.
I mean, without seeing any code we can't really say anything just based on that error message. It simply says that the compiler ran out of available EVM slots but not why it happened.
And before jumping into the code, we should make sure it's not one of the cases where the problem is still known to exist. I.e. are you using viaIR: true? Is the function non-recursive? Are your assembly blocks "memory-safe"? If either is not true, you may still run into this and it's not really a bug in the compiler, more like a limitation of the compiler in overcoming the limitations of the EVM.