Skip to content

Commit 993d7b8

Browse files
authored
Add irMinimum option (#907)
1 parent e2126e7 commit 993d7b8

File tree

8 files changed

+113
-7
lines changed

8 files changed

+113
-7
lines changed

README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ module.exports = {
9595
| Option <img width=200/>| Type <img width=200/> | Default <img width=1000/> | Description <img width=1000/> |
9696
| ------ | ---- | ------- | ----------- |
9797
| skipFiles | *Array* | `[]` | Array of contracts or folders (with paths expressed relative to the `contracts` directory) that should be skipped when doing instrumentation.(ex: `[ "Routers", "Networks/Polygon.sol"]`) :warning: **RUN THE HARDHAT CLEAN COMMAND AFTER UPDATING THIS** |
98+
| irMinimum | *Boolean* | `[]` | Speeds up test execution times when solc is run in `viaIR` mode. If your project successfully compiles while generating coverage with this option turned on (it may not!) it's worth using |
9899
| modifierWhitelist | *String[]* | `[]` | List of modifier names (ex: `onlyOwner`) to exclude from branch measurement. (Useful for modifiers which prepare something instead of acting as a gate.)) |
99100
| mocha | *Object* | `{ }` | [Mocha options][3] to merge into existing mocha config. `grep` and `invert` are useful for skipping certain tests under coverage using tags in the test descriptions. [More...][24]|
100101
| measureStatementCoverage | *boolean* | `true` | Computes statement (in addition to line) coverage. [More...][34] |
@@ -113,9 +114,9 @@ module.exports = {
113114
| onCompileComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* compilation completes, *before* tests are run. Useful if you have secondary compilation steps or need to modify built artifacts. [More...][23]|
114115
| onTestsComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* the tests complete, *before* Istanbul reports are generated. [More...][23]|
115116
| onIstanbulComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* the Istanbul reports are generated, *before* the coverage task completes. Useful if you need to clean resources up. [More...][23]|
116-
| **:warning: DEPRECATED** | | | |
117-
| configureYulOptimizer | *Boolean* | false | **(Deprecated since 0.8.7)** Setting to `true` should resolve "stack too deep" compiler errors in large projects using ABIEncoderV2 |
118-
| solcOptimizerDetails | *Object* | `undefined` |**(Deprecated since 0.8.7))** Must be used in combination with `configureYulOptimizer`. Allows you to configure solc's [optimizer details][1001]. Useful if the default remedy for stack-too-deep errors doesn't work in your case (See [FAQ: Running out of stack][1002] ). |
117+
| **:warning: LOW LEVEL** | | | |
118+
| configureYulOptimizer | *Boolean* | false | Setting to `true` lets you specify optimizer details (see next option). If no details are defined it defaults to turning on the yul optimizer and enabling stack allocation |
119+
| solcOptimizerDetails | *Object* | `undefined` |Must be used in combination with `configureYulOptimizer`. Allows you to configure solc's [optimizer details][1001]. (See [FAQ: Running out of stack][1002] ). |
119120

120121

121122
[<sup>*</sup> Advanced use][14]

lib/api.js

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class API {
5656

5757
this.viaIR = config.viaIR;
5858
this.usingSolcV4 = config.usingSolcV4;
59+
this.irMinimum = config.irMinimum;
5960
this.solcOptimizerDetails = config.solcOptimizerDetails;
6061

6162
this.setLoggingLevel(config.silent);

plugins/hardhat.plugin.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const {
1515
// Toggled true for `coverage` task only.
1616
let measureCoverage = false;
1717
let configureYulOptimizer = false;
18+
let irMinimum = false;
1819
let instrumentedSources;
1920
let optimizerDetails;
2021

@@ -70,12 +71,22 @@ subtask(TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOB_FOR_FILE).setAction(async (_,
7071

7172
// Beginning with v0.8.7, we let the optimizer run if viaIR is true and
7273
// instrument using `abi.encode(bytes8 covHash)`. Otherwise turn the optimizer off.
73-
if (!settings.viaIR) settings.optimizer.enabled = false;
74-
75-
// This sometimes fixed a stack-too-deep bug in ABIEncoderV2 for coverage plugin versions up to 0.8.6
74+
if (!settings.viaIR || irMinimum) settings.optimizer.enabled = false;
75+
76+
// Almost identical to foundry's irMinimum option - may improve performance for projects compiling with viaIR
77+
// Original discussion at: https://github.com/ethereum/solidity/issues/12533#issuecomment-1013073350
78+
if (irMinimum) {
79+
settings.optimizer.details = {
80+
yul: true,
81+
yulDetails: {
82+
stackAllocation: true,
83+
optimizerSteps: "",
84+
},
85+
}
86+
// LEGACY: This sometimes fixed a stack-too-deep bug in ABIEncoderV2 for coverage plugin versions up to 0.8.6
7687
// Although issue should be fixed in 0.8.7, am leaving this option in because it may still be necessary
7788
// to configure optimizer details in some cases.
78-
if (configureYulOptimizer) {
89+
} else if (configureYulOptimizer) {
7990
if (optimizerDetails === undefined) {
8091
settings.optimizer.details = {
8192
yul: true,
@@ -142,6 +153,7 @@ task("coverage", "Generates a code coverage report for tests")
142153
api = new API(utils.loadSolcoverJS(config));
143154

144155
optimizerDetails = api.solcOptimizerDetails;
156+
irMinimum = api.irMinimum;
145157

146158
// Catch interrupt signals
147159
process.on("SIGINT", nomiclabsUtils.finish.bind(null, config, api, true));

test/integration/standard.js

+16
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,22 @@ describe('Hardhat Plugin: standard use cases', function() {
380380
verify.lineCoverage(expected);
381381
})
382382

383+
it('compiles with irMinimum setting', async function(){
384+
mock.installFullProject('irMinimum');
385+
mock.hardhatSetupEnv(this);
386+
387+
await this.env.run("coverage");
388+
389+
const expected = [
390+
{
391+
file: mock.pathToContract(hardhatConfig, 'IRMinimum.sol'),
392+
pct: 100
393+
}
394+
];
395+
396+
verify.lineCoverage(expected);
397+
})
398+
383399
it('locates .coverage_contracts correctly when dir is subfolder', async function(){
384400
mock.installFullProject('contract-subfolders');
385401
mock.hardhatSetupEnv(this);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
silent: process.env.SILENT ? true : false,
3+
istanbulReporter: ['json-summary', 'text'],
4+
irMinimum: true,
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
pragma solidity >=0.8.0 <0.9.0;
2+
3+
contract ContractA {
4+
// 15 fn args + 1 local variable assignment
5+
// will trigger stack too deep error when optimizer is off.
6+
function stackTooDeep(
7+
uint _a,
8+
uint _b,
9+
uint _c,
10+
uint _d,
11+
uint _e,
12+
uint _f,
13+
uint _g,
14+
uint _h,
15+
uint _i,
16+
uint _j,
17+
uint _k,
18+
uint _l,
19+
uint _m,
20+
uint _n,
21+
uint _o
22+
) public {
23+
uint x = _a;
24+
}
25+
}
26+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
require("@nomiclabs/hardhat-truffle5");
2+
require(__dirname + "/../plugins/nomiclabs.plugin");
3+
4+
module.exports = {
5+
solidity: {
6+
version: "0.8.28",
7+
settings: {
8+
optimizer: {
9+
enabled: true
10+
},
11+
evmVersion: "cancun",
12+
viaIR: true
13+
}
14+
},
15+
logger: process.env.SILENT ? { log: () => {} } : console,
16+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const ContractA = artifacts.require("ContractA");
2+
3+
contract("contracta", function(accounts) {
4+
let a,b;
5+
6+
before(async () => {
7+
a = await ContractA.new();
8+
})
9+
10+
it('a:stackTooDeep', async function(){
11+
await a.stackTooDeep(
12+
1,
13+
2,
14+
3,
15+
4,
16+
5,
17+
6,
18+
7,
19+
8,
20+
9,
21+
10,
22+
11,
23+
12,
24+
13,
25+
14,
26+
15
27+
);
28+
})
29+
});

0 commit comments

Comments
 (0)