Skip to content

Commit 2b7ec23

Browse files
authored
Merge pull request #14562 from ethereum/fix-viaIR-codegen-import-bug
Fix Via-IR bytecode divergence when compiling multiple files that are already included via imports
2 parents cc7a14a + e3b36f7 commit 2b7ec23

File tree

10 files changed

+35
-36
lines changed

10 files changed

+35
-36
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Compiler Features:
1212

1313
Bugfixes:
1414
* AST: Fix wrong initial ID for Yul nodes in the AST.
15+
* Code Generator: Fix output from via-IR code generator being dependent on which files were discovered by import callback. In some cases, a different AST ID assignment would alter the order of functions in internal dispatch, resulting in superficially different but semantically equivalent bytecode.
1516
* NatSpec: Fix internal error when requesting userdoc or devdoc for a contract that emits an event defined in a foreign contract or interface.
1617
* SMTChecker: Fix encoding error that causes loops to unroll after completion.
1718
* SMTChecker: Fix inconsistency on constant condition checks when ``while`` or ``for`` loops are unrolled before the condition check.

libsolidity/codegen/ir/IRGenerationContext.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <libsolutil/StringUtils.h>
3232

3333
#include <range/v3/view/map.hpp>
34+
#include <range/v3/algorithm/find.hpp>
3435

3536
using namespace solidity;
3637
using namespace solidity::util;
@@ -41,7 +42,7 @@ std::string IRGenerationContext::enqueueFunctionForCodeGeneration(FunctionDefini
4142
std::string name = IRNames::function(_function);
4243

4344
if (!m_functions.contains(name))
44-
m_functionGenerationQueue.insert(&_function);
45+
m_functionGenerationQueue.push_back(&_function);
4546

4647
return name;
4748
}
@@ -50,8 +51,8 @@ FunctionDefinition const* IRGenerationContext::dequeueFunctionForCodeGeneration(
5051
{
5152
solAssert(!m_functionGenerationQueue.empty(), "");
5253

53-
FunctionDefinition const* result = *m_functionGenerationQueue.begin();
54-
m_functionGenerationQueue.erase(m_functionGenerationQueue.begin());
54+
FunctionDefinition const* result = m_functionGenerationQueue.front();
55+
m_functionGenerationQueue.pop_front();
5556
return result;
5657
}
5758

@@ -132,7 +133,7 @@ void IRGenerationContext::initializeInternalDispatch(InternalDispatchMap _intern
132133
{
133134
solAssert(internalDispatchClean(), "");
134135

135-
for (DispatchSet const& functions: _internalDispatch | ranges::views::values)
136+
for (DispatchQueue const& functions: _internalDispatch | ranges::views::values)
136137
for (auto function: functions)
137138
enqueueFunctionForCodeGeneration(*function);
138139

@@ -149,16 +150,15 @@ InternalDispatchMap IRGenerationContext::consumeInternalDispatchMap()
149150
void IRGenerationContext::addToInternalDispatch(FunctionDefinition const& _function)
150151
{
151152
FunctionType const* functionType = TypeProvider::function(_function, FunctionType::Kind::Internal);
152-
solAssert(functionType, "");
153+
solAssert(functionType);
153154

154155
YulArity arity = YulArity::fromType(*functionType);
155-
156-
if (m_internalDispatchMap.count(arity) != 0 && m_internalDispatchMap[arity].count(&_function) != 0)
157-
// Note that m_internalDispatchMap[arity] is a set with a custom comparator, which looks at function IDs not definitions
158-
solAssert(*m_internalDispatchMap[arity].find(&_function) == &_function, "Different definitions with the same function ID");
159-
160-
m_internalDispatchMap[arity].insert(&_function);
161-
enqueueFunctionForCodeGeneration(_function);
156+
DispatchQueue& dispatchQueue = m_internalDispatchMap[arity];
157+
if (ranges::find(dispatchQueue, &_function) == ranges::end(dispatchQueue))
158+
{
159+
dispatchQueue.push_back(&_function);
160+
enqueueFunctionForCodeGeneration(_function);
161+
}
162162
}
163163

164164

libsolidity/codegen/ir/IRGenerationContext.h

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,15 @@
3737
#include <set>
3838
#include <string>
3939
#include <memory>
40-
#include <vector>
4140

4241
namespace solidity::frontend
4342
{
4443

4544
class YulUtilFunctions;
4645
class ABIFunctions;
4746

48-
struct AscendingFunctionIDCompare
49-
{
50-
bool operator()(FunctionDefinition const* _f1, FunctionDefinition const* _f2) const
51-
{
52-
// NULLs always first.
53-
if (_f1 != nullptr && _f2 != nullptr)
54-
return _f1->id() < _f2->id();
55-
else
56-
return _f1 == nullptr;
57-
}
58-
};
59-
60-
using DispatchSet = std::set<FunctionDefinition const*, AscendingFunctionIDCompare>;
61-
using InternalDispatchMap = std::map<YulArity, DispatchSet>;
47+
using DispatchQueue = std::deque<FunctionDefinition const*>;
48+
using InternalDispatchMap = std::map<YulArity, DispatchQueue>;
6249

6350
/**
6451
* Class that contains contextual information during IR generation.
@@ -197,10 +184,9 @@ class IRGenerationContext
197184
/// were discovered by the IR generator during AST traversal.
198185
/// Note that the queue gets filled in a lazy way - new definitions can be added while the
199186
/// collected ones get removed and traversed.
200-
/// The order and duplicates are irrelevant here (hence std::set rather than std::queue) as
201-
/// long as the order of Yul functions in the generated code is deterministic and the same on
202-
/// all platforms - which is a property guaranteed by MultiUseYulFunctionCollector.
203-
DispatchSet m_functionGenerationQueue;
187+
/// The order and duplicates are relevant here
188+
/// (see: IRGenerationContext::[enqueue|dequeue]FunctionForCodeGeneration)
189+
DispatchQueue m_functionGenerationQueue;
204190

205191
/// Collection of functions that need to be callable via internal dispatch.
206192
/// Note that having a key with an empty set of functions is a valid situation. It means that

libsolidity/codegen/ir/IRGenerator.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefin
279279
templ("out", suffixedVariableNameList("out_", 0, arity.out));
280280

281281
std::vector<std::map<std::string, std::string>> cases;
282+
std::set<int64_t> caseValues;
282283
for (FunctionDefinition const* function: internalDispatchMap.at(arity))
283284
{
284285
solAssert(function, "");
@@ -289,12 +290,14 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefin
289290
solAssert(!function->isConstructor(), "");
290291
// 0 is reserved for uninitialized function pointers
291292
solAssert(function->id() != 0, "Unexpected function ID: 0");
293+
solAssert(caseValues.count(function->id()) == 0, "Duplicate function ID");
292294
solAssert(m_context.functionCollector().contains(IRNames::function(*function)), "");
293295

294296
cases.emplace_back(std::map<std::string, std::string>{
295297
{"funID", std::to_string(m_context.mostDerivedContract().annotation().internalFunctionIDs.at(function))},
296298
{"name", IRNames::function(*function)}
297299
});
300+
caseValues.insert(function->id());
298301
}
299302

300303
templ("cases", std::move(cases));

test/libsolidity/semanticTests/abiEncoderV1/abi_encode_calldata_slice.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,6 @@ contract C {
6363
// gas legacy: 411269
6464
// gas legacyOptimized: 317754
6565
// test_uint256() ->
66-
// gas irOptimized: 509677
66+
// gas irOptimized: 509479
6767
// gas legacy: 577469
6868
// gas legacyOptimized: 441003

test/libsolidity/semanticTests/abiEncoderV2/abi_encode_calldata_slice.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,6 @@ contract C {
6464
// gas legacy: 411269
6565
// gas legacyOptimized: 317754
6666
// test_uint256() ->
67-
// gas irOptimized: 509677
67+
// gas irOptimized: 509479
6868
// gas legacy: 577469
6969
// gas legacyOptimized: 441003

test/libsolidity/semanticTests/externalContracts/base64.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ contract test {
3333
// EVMVersion: >=constantinople
3434
// ----
3535
// constructor()
36-
// gas irOptimized: 406679
36+
// gas irOptimized: 406691
3737
// gas legacy: 737652
3838
// gas legacyOptimized: 527036
3939
// encode_inline_asm(bytes): 0x20, 0 -> 0x20, 0

test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ contract test {
4848
}
4949
// ----
5050
// constructor()
51-
// gas irOptimized: 1722598
51+
// gas irOptimized: 1722610
5252
// gas legacy: 2210160
5353
// gas legacyOptimized: 1734152
5454
// div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328

test/libsolidity/semanticTests/externalContracts/strings.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ contract test {
4949
}
5050
// ----
5151
// constructor()
52-
// gas irOptimized: 634316
52+
// gas irOptimized: 634328
5353
// gas legacy: 1065857
5454
// gas legacyOptimized: 725423
5555
// toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
contract C {
2+
function a() internal {}
3+
function f() public {
4+
function() ptr1 = a;
5+
function() ptr2 = a;
6+
}
7+
}
8+
// ----
9+
// f()

0 commit comments

Comments
 (0)