Repository navigation
List scheduler asserts when a later result of a multi-value call feeds a loop phi #178
Description
The EVM backend asserts in the SelectionDAG list scheduler when a non-first element of a multi-value fastcc call result flows into a loop phi. On solx-llvm 0ed778b:
target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256"
target triple = "evm-unknown-unknown"
declare fastcc { i256, i256 } @g(i256)
declare fastcc void @h(i256)
define fastcc void @f() {
entry:
br label %loop
loop:
%p = phi i256 [ %r, %loop ], [ 0, %entry ]
%s = call fastcc { i256, i256 } @g(i256 %p)
%r = extractvalue { i256, i256 } %s, 1
tail call fastcc void @h(i256 %p)
br label %loop
}$ llc -O3 repro.ll -o /dev/null
llc: llvm/include/llvm/ADT/ArrayRef.h:253: ... ArrayRef<unsigned short>::operator[](size_t) const: Assertion `Index < Length && "Invalid index!"' failed.
#9 (anonymous namespace)::RegReductionPQBase::initNodes(...) ScheduleDAGRRList.cpp
#10 (anonymous namespace)::ScheduleDAGRRList::Schedule()
#11 llvm::SelectionDAGISel::CodeGenAndEmitDAG()
It fails for any result arity of two or more when the extracted index is 1 or higher. With extractvalue ..., 0, or at -O0, it compiles.
The Slang frontend produces this shape for internal functions with several return values that are called in a loop. This contract hits it:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Repro {
mapping(uint256 => uint256) next;
mapping(uint256 => uint256) weight;
event Seen(uint256 from, uint256 to);
function step(uint256 cursor) internal returns (uint256 seen, uint256 following) {
following = cursor;
for (uint256 i = 0; i < 8; i++) {
following = next[following];
if (following == 0) break;
weight[following] += i;
emit Seen(cursor, following);
seen++;
}
for (uint256 i = 0; i < seen; i++) {
weight[cursor + i] = weight[following] / (i + 1);
emit Seen(following, weight[cursor + i]);
}
}
function walk(uint256 cursor) internal {
while (cursor != 0) {
(, uint256 following) = step(cursor);
step(cursor);
cursor = following;
}
}
function w1(uint256 c) external { walk(c); }
function w2(uint256 c) external { walk(c + 1); }
function a1(uint256 c) external returns (uint256, uint256) { return step(c); }
function a2(uint256 c) external returns (uint256, uint256) { return step(c + 1); }
}$ solx --bin Repro.sol
solx: llvm/include/llvm/ADT/ArrayRef.h:253: ... Assertion `Index < Length && "Invalid index!"' failed.
Error: The worker exited without replying: signal: 6 (SIGABRT) (core dumped)
step has to stay out of line, which is why it has a second loop and extra callers. The old cursor also has to be passed to a call after the one that produces following. The original is ph_distribution in GH_MLM_V3.sol (Polygon, 0x3768bd82b8f0eec396f2e3336bc2efa742eaebad). It walks a list with (,_type, next_no,) = marketplaceFeed(_type, no, 1), calls checkAndDetach(no), then sets no = next_no.
It accounts for 29 failures in the Sourcify sweep (NomicFoundation/solx#685). The solc-frontend 0.1.8 compiles all of them, including the contract above.
Activity