Skip to content

feat(slang): checked arithmetic, narrow types, and MLIR alignment #993

feat(slang): checked arithmetic, narrow types, and MLIR alignment

feat(slang): checked arithmetic, narrow types, and MLIR alignment #993

Workflow file for this run

name: Code coverage
on:
pull_request:
types: [opened, synchronize, reopened, labeled]
concurrency:
group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: "bash -ex {0}"
permissions:
contents: read
jobs:
label-check:
if: >-
(github.event.action == 'labeled' && github.event.label.name == 'ci:coverage'
|| github.event.action != 'labeled' && contains(github.event.pull_request.labels.*.name, 'ci:coverage'))
&& !github.event.pull_request.head.repo.fork
runs-on: ubuntu-24.04
steps:
- run: 'true'
cooldown-check:
name: Cargo cooldown check
runs-on: ubuntu-24.04
needs: label-check
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: ./.github/actions/cooldown-check
coverage:
needs: [label-check, cooldown-check]
runs-on: solx-linux-amd64
permissions:
contents: read
pull-requests: write
packages: read
container:
image: ghcr.io/nomicfoundation/solx-ci-runner@sha256:c0866d146261cd0a51dc7d9077444b8ac3dde12c53d2151137834e6be149dbc7
env:
BOOST_PREFIX: ${{ github.workspace }}/solx-solidity/boost/lib
SOLC_PREFIX: ${{ github.workspace }}/solx-solidity/build
PROFDATA_FILE: solx.profdata
LCOV_FILE: codecov.lcov
OUTPUT_HTML_DIR: COVERAGE
COVERAGE_IGNORE_REGEX: '/(solx-llvm|target-llvm|solx-solidity|\.cargo|cargo/registry)/'
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: true
- name: Checkout submodules
run: |
git config --global --add safe.directory '*'
git submodule update --init --force --depth=1 --recursive --checkout
- name: Build LLVM
uses: ./.github/actions/build-llvm
with:
build-type: 'RelWithDebInfo'
enable-assertions: 'false'
enable-coverage: 'true'
enable-mlir: 'true'
- name: Building solc
uses: ./.github/actions/build-solc
with:
cmake-build-type: RelWithDebInfo
working-dir: 'solx-solidity'
- name: Build solx
id: build-solx
uses: ./.github/actions/build-rust
with:
exec_name: 'solx'
build-type: 'release'
enable-coverage: 'true'
target: 'x86_64-unknown-linux-gnu'
- name: Run integration tests
run: |
TMP="${PROFDATA_FILE}.tmp"
TARGET_DIR="./target/x86_64-unknown-linux-gnu/release"
for TEST_PATH in tests/solidity/simple/default.sol tests/solidity/complex/default; do
WORKDIR=$(basename ${TEST_PATH})
mkdir -p "${GITHUB_WORKSPACE}/${WORKDIR}"
"${TARGET_DIR}/solx-dev" test solx-tester \
--binary "${TARGET_DIR}/solx-tester" \
--solidity-compiler ${{ steps.build-solx.outputs.binary-path }} \
--path ${TEST_PATH} --workflow build --optimizer M3B3
mv *.profraw "${GITHUB_WORKSPACE}/${WORKDIR}/" || true
find "${GITHUB_WORKSPACE}/${WORKDIR}" -type f -name '*.profraw' -print > profiles.lst
if [[ -f "${PROFDATA_FILE}" ]]; then
llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" "${PROFDATA_FILE}" @profiles.lst
else
llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" @profiles.lst
fi
mv -f "${TMP}" "${PROFDATA_FILE}"
rm -rf "${GITHUB_WORKSPACE}/${WORKDIR}"
done
- name: Preserve instrumented binary
env:
SOLX_BINARY: ${{ steps.build-solx.outputs.binary-path }}
run: |
cp "${SOLX_BINARY}" "${GITHUB_WORKSPACE}/solx-instrumented"
cp "./target/x86_64-unknown-linux-gnu/release/solx-tester" "${GITHUB_WORKSPACE}/solx-tester-instrumented"
cp "./target/x86_64-unknown-linux-gnu/release/solx-dev" "${GITHUB_WORKSPACE}/solx-dev-instrumented"
- name: Run unit tests
env:
RUSTC_BOOTSTRAP: 1
RUSTFLAGS: '-C target-feature=+crt-static -C instrument-coverage -C link-arg=-fuse-ld=lld'
LLVM_PROFILE_FILE: 'unit-tests-%p-%m.profraw'
run: cargo fetch --locked && cargo test --frozen --release --target x86_64-unknown-linux-gnu
# Inkwell feature poisoning: solc path builds inkwell with LLVM linking,
# slang path needs no-llvm-linking. cargo clean cannot reliably clear
# cached llvm-sys build script output. Use a separate target directory
# for complete isolation.
- name: Run slang unit tests
if: contains(github.event.pull_request.labels.*.name, 'ci:slang')
env:
RUSTC_BOOTSTRAP: 1
RUSTFLAGS: '-C instrument-coverage -C link-arg=-fuse-ld=lld'
LLVM_PROFILE_FILE: 'slang-tests-%p-%m.profraw'
CARGO_TARGET_DIR: target-slang
run: cargo test-slang --frozen --release --target x86_64-unknown-linux-gnu
- name: Preserve slang instrumented binary
if: contains(github.event.pull_request.labels.*.name, 'ci:slang')
run: cp "./target-slang/x86_64-unknown-linux-gnu/release/solx" "${GITHUB_WORKSPACE}/solx-slang-instrumented"
- name: Merge slang test profraw
if: contains(github.event.pull_request.labels.*.name, 'ci:slang')
run: |
find . -name 'slang-tests-*.profraw' -print > slang-profiles.lst
if [ -s slang-profiles.lst ]; then
TMP="${PROFDATA_FILE}.tmp"
llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" "${PROFDATA_FILE}" @slang-profiles.lst
mv -f "${TMP}" "${PROFDATA_FILE}"
find . -name 'slang-tests-*.profraw' -delete
fi
- name: Merge unit test profraw
run: |
find . -name 'unit-tests-*.profraw' -print > unit-profiles.lst
if [ -s unit-profiles.lst ]; then
TMP="${PROFDATA_FILE}.tmp"
llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" "${PROFDATA_FILE}" @unit-profiles.lst
mv -f "${TMP}" "${PROFDATA_FILE}"
find . -name 'unit-tests-*.profraw' -delete
fi
- name: Generate coverage reports
run: |
OBJECTS=(
-object "${GITHUB_WORKSPACE}/solx-instrumented"
-object "${GITHUB_WORKSPACE}/solx-tester-instrumented"
-object "${GITHUB_WORKSPACE}/solx-dev-instrumented"
)
[ -f "${GITHUB_WORKSPACE}/solx-slang-instrumented" ] && OBJECTS+=(-object "${GITHUB_WORKSPACE}/solx-slang-instrumented")
llvm-cov show --show-directory-coverage \
--format=html --output-dir=${OUTPUT_HTML_DIR} \
--ignore-filename-regex="${COVERAGE_IGNORE_REGEX}" \
-instr-profile=${PROFDATA_FILE} "${OBJECTS[@]}"
mkdir -p llvm
llvm-cov export --format=lcov \
--ignore-filename-regex="${COVERAGE_IGNORE_REGEX}" \
-instr-profile=${PROFDATA_FILE} \
"${OBJECTS[@]}" > ./llvm/${LCOV_FILE}
- name: Coverage summary
id: coverage-summary
run: |
OBJECTS=(
-object "${GITHUB_WORKSPACE}/solx-instrumented"
-object "${GITHUB_WORKSPACE}/solx-tester-instrumented"
-object "${GITHUB_WORKSPACE}/solx-dev-instrumented"
)
[ -f "${GITHUB_WORKSPACE}/solx-slang-instrumented" ] && OBJECTS+=(-object "${GITHUB_WORKSPACE}/solx-slang-instrumented")
llvm-cov report -instr-profile=${PROFDATA_FILE} "${OBJECTS[@]}" \
--ignore-filename-regex="${COVERAGE_IGNORE_REGEX}" \
> coverage-report.txt
# Parse llvm-cov report into per-crate markdown table
awk '
/^---/ || /^Filename/ || /^$/ { next }
/^TOTAL/ {
total_lines = $(NF-5); total_missed = $(NF-4)
total_funcs = $(NF-8); total_missed_funcs = $(NF-7)
next
}
NF > 3 {
file = $1
n = split(file, parts, "/")
crate = ""
for (i = 1; i <= n-1; i++) {
if (parts[i] ~ /^solx/ && parts[i+1] == "src") {
crate = parts[i]; break
}
}
if (crate == "") next
lines[crate] += $(NF-5)
missed[crate] += $(NF-4)
funcs[crate] += $(NF-8)
missed_f[crate] += $(NF-7)
}
END {
for (c in lines) {
lp = (lines[c] > 0) ? (lines[c] - missed[c]) / lines[c] * 100 : 0
fp = (funcs[c] > 0) ? (funcs[c] - missed_f[c]) / funcs[c] * 100 : 0
le = (lp >= 80) ? "🟒" : (lp >= 50) ? "🟑" : "πŸ”΄"
fe = (fp >= 80) ? "🟒" : (fp >= 50) ? "🟑" : "πŸ”΄"
printf "| %s | %s %.1f%% | %s %.1f%% |\n", c, le, lp, fe, fp
}
lp = (total_lines > 0) ? (total_lines - total_missed) / total_lines * 100 : 0
fp = (total_funcs > 0) ? (total_funcs - total_missed_funcs) / total_funcs * 100 : 0
le = (lp >= 80) ? "🟒" : (lp >= 50) ? "🟑" : "πŸ”΄"
fe = (fp >= 80) ? "🟒" : (fp >= 50) ? "🟑" : "πŸ”΄"
printf "ZTOTAL| **Total** | **%s %.1f%%** | **%s %.1f%%** |\n", le, lp, fe, fp
}' coverage-report.txt > coverage-rows.txt
{
echo "| Crate | Line Coverage | Function Coverage |"
echo "|:------|:------------:|:-----------------:|"
grep -v '^ZTOTAL' coverage-rows.txt | sort
sed -n 's/^ZTOTAL//p' coverage-rows.txt
} > coverage-summary.md
cat coverage-summary.md >> "${GITHUB_STEP_SUMMARY}"
{
echo "summary<<COVERAGE_EOF"
cat coverage-summary.md
echo "COVERAGE_EOF"
} >> "${GITHUB_OUTPUT}"
- name: Post PR coverage summary
uses: mshick/add-pr-comment@ffd016c7e151d97d69d21a843022fd4cd5b96fe5 # v3.9.0
with:
message-id: 'coverage-summary'
message: |
### Coverage Summary
${{ steps.coverage-summary.outputs.summary }}
[Codecov Report](https://app.codecov.io/gh/${{ github.repository }}/pull/${{ github.event.pull_request.number }}) | [HTML Report](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}#artifacts) | [Workflow Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
- name: Upload coverage artifacts
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: 'Coverage integration tests HTML'
path: ${{ env.OUTPUT_HTML_DIR }}
- name: Upload coverage to Codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
if: (success() || failure())
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: 'llvm/codecov.lcov'
slug: ${{ github.repository }}
fail_ci_if_error: false