mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-14 22:41:07 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0685df37a1 | |||
| 3204b61ea0 | |||
| 4028f7a985 |
@@ -1,13 +1,3 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
This script generates JSON files for different platforms based on GitHub release data.
|
|
||||||
It fetches release information from a specified GitHub repository and tag,
|
|
||||||
parses the release assets, and generates JSON files for each platform with relevant metadata.
|
|
||||||
It also handles checksum files and updates a list.json file for each platform.
|
|
||||||
It requires the GITHUB_TOKEN environment variable to be set for authentication.
|
|
||||||
Usage:
|
|
||||||
python json_generator.py <repo> <tag>
|
|
||||||
"""
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
This script generates JSON files for different platforms based on GitHub data.
|
|
||||||
Requires the GITHUB_SHA, FIRST_SOLC_VERSION, LAST_SOLC_VERSION, TAG and FILEPATH environment variables to be set.
|
|
||||||
Usage:
|
|
||||||
python json_generator_nightly.py
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
def validate_env_variables():
|
|
||||||
"""Validate that environment variables are set."""
|
|
||||||
if "GITHUB_SHA" not in os.environ:
|
|
||||||
print("Error: GITHUB_SHA environment variable is not set.")
|
|
||||||
sys.exit(1)
|
|
||||||
if "FIRST_SOLC_VERSION" not in os.environ:
|
|
||||||
print("Error: FIRST_SOLC_VERSION environment variable is not set.")
|
|
||||||
sys.exit(1)
|
|
||||||
if "LAST_SOLC_VERSION" not in os.environ:
|
|
||||||
print("Error: LAST_SOLC_VERSION environment variable is not set.")
|
|
||||||
sys.exit(1)
|
|
||||||
if "TAG" not in os.environ:
|
|
||||||
print("Error: TAG environment variable is not set.")
|
|
||||||
sys.exit(1)
|
|
||||||
if "FILEPATH" not in os.environ:
|
|
||||||
print("Error: FILEPATH environment variable is not set.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_data_file():
|
|
||||||
"""
|
|
||||||
Fetch the data.json file with artifacts urls and sha256 checksums
|
|
||||||
and parse it into a single dictionary mapping artifact names to their URLs and SHAs.
|
|
||||||
"""
|
|
||||||
# read data.json file
|
|
||||||
artifacts_data = {}
|
|
||||||
data_file_path = os.environ["FILEPATH"]
|
|
||||||
if not os.path.exists(data_file_path):
|
|
||||||
print("Error: data.json file not found.")
|
|
||||||
sys.exit(1)
|
|
||||||
with open(data_file_path, 'r') as f:
|
|
||||||
try:
|
|
||||||
artifacts_data = json.load(f)
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
print("Error: data.json file is not a valid JSON.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
for item in artifacts_data:
|
|
||||||
for key, value in item.items():
|
|
||||||
if key.endswith('_url'):
|
|
||||||
base_key = key.rsplit('_url', 1)[0]
|
|
||||||
if base_key not in result:
|
|
||||||
result[base_key] = {}
|
|
||||||
result[base_key]['url'] = value
|
|
||||||
elif key.endswith('_sha'):
|
|
||||||
base_key = key.rsplit('_sha', 1)[0]
|
|
||||||
if base_key not in result:
|
|
||||||
result[base_key] = {}
|
|
||||||
result[base_key]['sha'] = value
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def extract_build_hash():
|
|
||||||
"""Extract the first 8 characters of the commit hash."""
|
|
||||||
sha = os.environ.get("GITHUB_SHA")
|
|
||||||
return f"commit.{sha[:8]}"
|
|
||||||
|
|
||||||
def generate_asset_json_nightly(name, url, checksum):
|
|
||||||
"""Generate JSON for a specific asset."""
|
|
||||||
# Date in format YYYY-MM-DD
|
|
||||||
date = datetime.now().strftime("%Y.%-m.%-d")
|
|
||||||
last_version = os.environ.get("TAG").replace('v','')
|
|
||||||
version = f"{last_version}-nightly.{date}"
|
|
||||||
SHA = os.environ.get("GITHUB_SHA", "")[:8]
|
|
||||||
build = f"commit.{SHA}"
|
|
||||||
long_version = f"{version}+{build}"
|
|
||||||
|
|
||||||
return {
|
|
||||||
"name": name,
|
|
||||||
"version": version,
|
|
||||||
"build": build,
|
|
||||||
"longVersion": long_version,
|
|
||||||
"url": url,
|
|
||||||
"sha256": checksum,
|
|
||||||
"firstSolcVersion": os.environ.get("FIRST_SOLC_VERSION"),
|
|
||||||
"lastSolcVersion": os.environ.get("LAST_SOLC_VERSION")
|
|
||||||
}
|
|
||||||
|
|
||||||
def save_platform_json(platform_folder, asset_json):
|
|
||||||
"""Save asset JSON and update list.json for a specific platform."""
|
|
||||||
# Create platform folder if it doesn't exist
|
|
||||||
os.makedirs(platform_folder, exist_ok=True)
|
|
||||||
|
|
||||||
# Update or create list.json
|
|
||||||
list_file_path = os.path.join(platform_folder, "list.json")
|
|
||||||
|
|
||||||
if os.path.exists(list_file_path):
|
|
||||||
with open(list_file_path, 'r') as f:
|
|
||||||
try:
|
|
||||||
list_data = json.load(f)
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
list_data = {"builds": [], "releases": {}, "latestRelease": ""}
|
|
||||||
else:
|
|
||||||
list_data = {"builds": [], "releases": {}, "latestRelease": ""}
|
|
||||||
|
|
||||||
# Remove any existing entry with the same path
|
|
||||||
list_data['builds'] = [
|
|
||||||
build for build in list_data['builds']
|
|
||||||
if build['version'] != asset_json['version']
|
|
||||||
]
|
|
||||||
# Add the new build
|
|
||||||
list_data['builds'].append(asset_json)
|
|
||||||
|
|
||||||
# Update releases
|
|
||||||
version = asset_json['version']
|
|
||||||
list_data['releases'][version] = f"{asset_json['name']}+{asset_json['longVersion']}"
|
|
||||||
|
|
||||||
# Update latest release
|
|
||||||
list_data['latestRelease'] = version
|
|
||||||
|
|
||||||
with open(list_file_path, 'w') as f:
|
|
||||||
json.dump(list_data, f, indent=4)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
|
|
||||||
validate_env_variables()
|
|
||||||
data = fetch_data_file()
|
|
||||||
|
|
||||||
# Mapping of asset names to platform folders
|
|
||||||
platform_mapping = {
|
|
||||||
'resolc-x86_64-unknown-linux-musl': 'linux',
|
|
||||||
'resolc-universal-apple-darwin': 'macos',
|
|
||||||
'resolc-x86_64-pc-windows-msvc': 'windows',
|
|
||||||
'resolc-web.js': 'wasm'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Process each asset
|
|
||||||
for asset in data.keys():
|
|
||||||
platform_name = platform_mapping.get(asset)
|
|
||||||
if platform_name:
|
|
||||||
platform_folder = os.path.join(platform_name)
|
|
||||||
asset_json = generate_asset_json_nightly(asset, data[asset]['url'], data[asset]['sha'])
|
|
||||||
save_platform_json(platform_folder, asset_json)
|
|
||||||
print(f"Processed {asset} for {platform_name}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,385 +0,0 @@
|
|||||||
name: Nightly Release
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# Run every day at 01:00 UTC
|
|
||||||
- cron: "0 1 * * *"
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# check if there were commits yesterday
|
|
||||||
check_commits:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
has_commits: ${{ steps.check_commits.outputs.has_commits }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0 # Fetch full history to check previous commits
|
|
||||||
ref: "main"
|
|
||||||
|
|
||||||
- name: Check for commits from yesterday
|
|
||||||
id: check_commits
|
|
||||||
run: |
|
|
||||||
# Get yesterday's date in YYYY-MM-DD format
|
|
||||||
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
|
|
||||||
echo "Checking for commits from: $YESTERDAY"
|
|
||||||
|
|
||||||
# Check if there were any commits yesterday
|
|
||||||
COMMIT_COUNT=$(git log --oneline --since="$YESTERDAY 00:00:00" --until="$YESTERDAY 23:59:59" | wc -l)
|
|
||||||
|
|
||||||
echo "Found $COMMIT_COUNT commits from yesterday"
|
|
||||||
|
|
||||||
if [ $COMMIT_COUNT -gt 0 ]; then
|
|
||||||
echo "has_commits=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "✅ Found $COMMIT_COUNT commits from yesterday - continuing workflow"
|
|
||||||
else
|
|
||||||
echo "has_commits=false" >> $GITHUB_OUTPUT
|
|
||||||
echo "❌ No commits found from yesterday - skipping remaining steps"
|
|
||||||
echo "::notice::❌ No commits found from yesterday - skipping remaining steps"
|
|
||||||
fi
|
|
||||||
|
|
||||||
build:
|
|
||||||
# github actions matrix jobs don't support multiple outputs
|
|
||||||
# ugly workaround from https://github.com/orgs/community/discussions/17245#discussioncomment-11222880
|
|
||||||
if: ${{ needs.check_commits.outputs.has_commits == 'true' }}
|
|
||||||
outputs:
|
|
||||||
resolc-x86_64-unknown-linux-musl_url: ${{ steps.set-output.outputs.resolc-x86_64-unknown-linux-musl_url }}
|
|
||||||
resolc-x86_64-unknown-linux-musl_sha: ${{ steps.set-output.outputs.resolc-x86_64-unknown-linux-musl_sha }}
|
|
||||||
resolc-aarch64-apple-darwin_url: ${{ steps.set-output.outputs.resolc-aarch64-apple-darwin_url }}
|
|
||||||
resolc-aarch64-apple-darwin_sha: ${{ steps.set-output.outputs.resolc-aarch64-apple-darwin_sha }}
|
|
||||||
resolc-x86_64-apple-darwin_url: ${{ steps.set-output.outputs.resolc-x86_64-apple-darwin_url }}
|
|
||||||
resolc-x86_64-apple-darwin_sha: ${{ steps.set-output.outputs.resolc-x86_64-apple-darwin_sha }}
|
|
||||||
resolc-x86_64-pc-windows-msvc_url: ${{ steps.set-output.outputs.resolc-x86_64-pc-windows-msvc_url }}
|
|
||||||
resolc-x86_64-pc-windows-msvc_sha: ${{ steps.set-output.outputs.resolc-x86_64-pc-windows-msvc_sha }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
target:
|
|
||||||
[
|
|
||||||
x86_64-unknown-linux-musl,
|
|
||||||
aarch64-apple-darwin,
|
|
||||||
x86_64-apple-darwin,
|
|
||||||
x86_64-pc-windows-msvc,
|
|
||||||
]
|
|
||||||
include:
|
|
||||||
- target: x86_64-unknown-linux-musl
|
|
||||||
type: musl
|
|
||||||
runner: ubuntu-24.04
|
|
||||||
- target: aarch64-apple-darwin
|
|
||||||
type: native
|
|
||||||
runner: macos-14
|
|
||||||
- target: x86_64-apple-darwin
|
|
||||||
type: native
|
|
||||||
runner: macos-13
|
|
||||||
- target: x86_64-pc-windows-msvc
|
|
||||||
type: native
|
|
||||||
runner: windows-2022
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
needs: [check_commits]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
||||||
with:
|
|
||||||
# without this it will override our rust flags
|
|
||||||
rustflags: ""
|
|
||||||
cache-key: ${{ matrix.target }}
|
|
||||||
|
|
||||||
- name: Download LLVM
|
|
||||||
uses: ./.github/actions/get-llvm
|
|
||||||
with:
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
if: ${{ matrix.type == 'native' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
export LLVM_SYS_181_PREFIX=$PWD/llvm-${{ matrix.target }}
|
|
||||||
make install-bin
|
|
||||||
mv target/release/resolc resolc-${{ matrix.target }} || mv target/release/resolc.exe resolc-${{ matrix.target }}.exe
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
if: ${{ matrix.type == 'musl' }}
|
|
||||||
run: |
|
|
||||||
docker run -v $PWD:/opt/revive $RUST_MUSL_CROSS_IMAGE /bin/bash -c "
|
|
||||||
cd /opt/revive
|
|
||||||
chown -R root:root .
|
|
||||||
apt update && apt upgrade -y && apt install -y pkg-config
|
|
||||||
export LLVM_SYS_181_PREFIX=/opt/revive/llvm-${{ matrix.target }}
|
|
||||||
make install-bin
|
|
||||||
mv target/${{ matrix.target }}/release/resolc resolc-${{ matrix.target }}
|
|
||||||
"
|
|
||||||
sudo chown -R $(id -u):$(id -g) .
|
|
||||||
|
|
||||||
- name: Install Solc
|
|
||||||
uses: ./.github/actions/get-solc
|
|
||||||
|
|
||||||
- name: Basic Sanity Check
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
result=$(./resolc-${{ matrix.target }} --bin crates/integration/contracts/flipper.sol)
|
|
||||||
echo $result
|
|
||||||
if [[ $result == *'0x50564d'* ]]; then exit 0; else exit 1; fi
|
|
||||||
|
|
||||||
- name: Upload artifacts (nightly)
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
id: artifact-upload-step
|
|
||||||
with:
|
|
||||||
name: resolc-${{ matrix.target }}
|
|
||||||
path: resolc-${{ matrix.target }}*
|
|
||||||
retention-days: 40
|
|
||||||
|
|
||||||
- name: Set output variables (nightly)
|
|
||||||
id: set-output
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "Artifact URL is ${{ steps.artifact-upload-step.outputs.artifact-url }}"
|
|
||||||
echo "Artifact SHA is ${{ steps.artifact-upload-step.outputs.artifact-digest }}"
|
|
||||||
echo "resolc-${{ matrix.target }}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}"
|
|
||||||
echo "resolc-${{ matrix.target }}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "resolc-${{ matrix.target }}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}"
|
|
||||||
echo "resolc-${{ matrix.target }}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
build-wasm:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs: [check_commits]
|
|
||||||
if: ${{ needs.check_commits.outputs.has_commits == 'true' }}
|
|
||||||
outputs:
|
|
||||||
resolc-web.js_url: ${{ steps.set-output.outputs.resolc_web_js_url }}
|
|
||||||
resolc-web.js_sha: ${{ steps.set-output.outputs.resolc_web_js_sha }}
|
|
||||||
env:
|
|
||||||
RELEASE_RESOLC_WASM_URI: https://github.com/paritytech/revive/releases/download/${{ github.ref_name }}/resolc.wasm
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
||||||
with:
|
|
||||||
target: wasm32-unknown-emscripten
|
|
||||||
# without this it will override our rust flags
|
|
||||||
rustflags: ""
|
|
||||||
|
|
||||||
- name: Download Host LLVM
|
|
||||||
uses: ./.github/actions/get-llvm
|
|
||||||
with:
|
|
||||||
target: x86_64-unknown-linux-gnu
|
|
||||||
|
|
||||||
- name: Download Wasm LLVM
|
|
||||||
uses: ./.github/actions/get-llvm
|
|
||||||
with:
|
|
||||||
target: wasm32-unknown-emscripten
|
|
||||||
|
|
||||||
- name: Download EMSDK
|
|
||||||
uses: ./.github/actions/get-emsdk
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
export LLVM_SYS_181_PREFIX=$PWD/llvm-x86_64-unknown-linux-gnu
|
|
||||||
export REVIVE_LLVM_TARGET_PREFIX=$PWD/llvm-wasm32-unknown-emscripten
|
|
||||||
source emsdk/emsdk_env.sh
|
|
||||||
make install-wasm
|
|
||||||
chmod -x ./target/wasm32-unknown-emscripten/release/resolc.wasm
|
|
||||||
|
|
||||||
- name: Set Up Node.js
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: "20"
|
|
||||||
|
|
||||||
- name: Basic Sanity Check
|
|
||||||
run: |
|
|
||||||
mkdir -p solc
|
|
||||||
curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.30/soljson.js
|
|
||||||
node -e "
|
|
||||||
const soljson = require('solc/soljson');
|
|
||||||
const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js');
|
|
||||||
|
|
||||||
const compiler = createRevive();
|
|
||||||
compiler.soljson = soljson;
|
|
||||||
|
|
||||||
const standardJsonInput =
|
|
||||||
{
|
|
||||||
language: 'Solidity',
|
|
||||||
sources: {
|
|
||||||
'MyContract.sol': {
|
|
||||||
content: 'pragma solidity ^0.8.0; contract MyContract { function greet() public pure returns (string memory) { return \'Hello\'; } }',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
settings: { optimizer: { enabled: false } }
|
|
||||||
};
|
|
||||||
|
|
||||||
compiler.writeToStdin(JSON.stringify(standardJsonInput));
|
|
||||||
compiler.callMain(['--standard-json']);
|
|
||||||
|
|
||||||
// Collect output
|
|
||||||
const stdout = compiler.readFromStdout();
|
|
||||||
const stderr = compiler.readFromStderr();
|
|
||||||
|
|
||||||
if (stderr) { console.error(stderr); process.exit(1); }
|
|
||||||
|
|
||||||
let out = JSON.parse(stdout);
|
|
||||||
let bytecode = out.contracts['MyContract.sol']['MyContract'].evm.bytecode.object
|
|
||||||
console.log(bytecode);
|
|
||||||
|
|
||||||
if(!bytecode.startsWith('50564d')) { process.exit(1); }
|
|
||||||
"
|
|
||||||
|
|
||||||
- name: Compress Artifact
|
|
||||||
run: |
|
|
||||||
mkdir -p resolc-wasm32-unknown-emscripten
|
|
||||||
mv ./target/wasm32-unknown-emscripten/release/resolc.js ./resolc-wasm32-unknown-emscripten/
|
|
||||||
mv ./target/wasm32-unknown-emscripten/release/resolc.wasm ./resolc-wasm32-unknown-emscripten/
|
|
||||||
mv ./target/wasm32-unknown-emscripten/release/resolc_web.js ./resolc-wasm32-unknown-emscripten/
|
|
||||||
|
|
||||||
# There is no way to upload several files as several artifacts with a single upload-artifact step
|
|
||||||
# It's needed to have resolc_web.js separately for night builds for resolc-bin repo
|
|
||||||
# https://github.com/actions/upload-artifact/issues/331
|
|
||||||
- name: Upload artifact resolc.js (nightly)
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: resolc.js
|
|
||||||
path: resolc-wasm32-unknown-emscripten/resolc.js
|
|
||||||
retention-days: 40
|
|
||||||
|
|
||||||
- name: Upload artifacts resolc.wasm (nightly)
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: resolc.wasm
|
|
||||||
path: resolc-wasm32-unknown-emscripten/resolc.wasm
|
|
||||||
retention-days: 40
|
|
||||||
|
|
||||||
- name: Upload artifacts resolc_web.js (nightly)
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
id: artifact-upload-step
|
|
||||||
with:
|
|
||||||
name: resolc_web.js
|
|
||||||
path: resolc-wasm32-unknown-emscripten/resolc_web.js
|
|
||||||
retention-days: 40
|
|
||||||
|
|
||||||
- name: Set output variables
|
|
||||||
id: set-output
|
|
||||||
env:
|
|
||||||
TARGET: resolc_web_js
|
|
||||||
run: |
|
|
||||||
echo "Artifact URL is ${{ steps.artifact-upload-step.outputs.artifact-url }}"
|
|
||||||
echo "Artifact SHA is ${{ steps.artifact-upload-step.outputs.artifact-digest }}"
|
|
||||||
echo "${TARGET}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}"
|
|
||||||
echo "${TARGET}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "${TARGET}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}""
|
|
||||||
echo "${TARGET}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}"" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
create-macos-fat-binary:
|
|
||||||
if: ${{ needs.check_commits.outputs.has_commits == 'true' }}
|
|
||||||
needs: [build]
|
|
||||||
outputs:
|
|
||||||
resolc-universal-apple-darwin_url: ${{ steps.set-output.outputs.resolc-universal-apple-darwin_url }}
|
|
||||||
resolc-universal-apple-darwin_sha: ${{ steps.set-output.outputs.resolc-universal-apple-darwin_sha }}
|
|
||||||
runs-on: macos-14
|
|
||||||
steps:
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Create macOS Fat Binary
|
|
||||||
run: |
|
|
||||||
lipo resolc-aarch64-apple-darwin resolc-x86_64-apple-darwin -create -output resolc-universal-apple-darwin
|
|
||||||
|
|
||||||
- name: Make Executable
|
|
||||||
run: |
|
|
||||||
chmod +x resolc-universal-apple-darwin
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
id: artifact-upload-step
|
|
||||||
with:
|
|
||||||
name: resolc-universal-apple-darwin
|
|
||||||
path: resolc-universal-apple-darwin
|
|
||||||
retention-days: 40
|
|
||||||
|
|
||||||
- name: Set output variables
|
|
||||||
id: set-output
|
|
||||||
env:
|
|
||||||
TARGET: resolc-universal-apple-darwin
|
|
||||||
run: |
|
|
||||||
echo "Artifact URL is ${{ steps.artifact-upload-step.outputs.artifact-url }}"
|
|
||||||
echo "Artifact SHA is ${{ steps.artifact-upload-step.outputs.artifact-digest }}"
|
|
||||||
echo "${TARGET}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}"
|
|
||||||
echo "${TARGET}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "${TARGET}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}""
|
|
||||||
echo "${TARGET}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}"" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
generate-nightly-json:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
if: ${{ needs.check_commits.outputs.has_commits == 'true' }}
|
|
||||||
environment: tags
|
|
||||||
needs: [build-wasm, build, create-macos-fat-binary, check_commits]
|
|
||||||
steps:
|
|
||||||
- name: Checkout revive
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
path: revive
|
|
||||||
|
|
||||||
- name: Checkout resolc-bin
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: paritytech/resolc-bin
|
|
||||||
path: resolc-bin
|
|
||||||
|
|
||||||
- name: Download Artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
merge-multiple: true
|
|
||||||
path: bins
|
|
||||||
|
|
||||||
- uses: actions/create-github-app-token@v1
|
|
||||||
id: app-token
|
|
||||||
with:
|
|
||||||
app-id: ${{ secrets.REVIVE_JSON_APP_ID }}
|
|
||||||
private-key: ${{ secrets.REVIVE_JSON_APP_KEY }}
|
|
||||||
owner: paritytech
|
|
||||||
repositories: resolc-bin
|
|
||||||
|
|
||||||
- name: Generate JSON
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
TOKEN: ${{ steps.app-token.outputs.token }}
|
|
||||||
APP_NAME: "paritytech-revive-json"
|
|
||||||
Green: "\e[32m"
|
|
||||||
NC: "\e[0m"
|
|
||||||
run: |
|
|
||||||
echo '[' > data.json
|
|
||||||
echo '${{ toJSON(needs.build.outputs) }}' >> data.json
|
|
||||||
echo ',' >> data.json
|
|
||||||
echo '${{ toJSON(needs.build-wasm.outputs) }}' >> data.json
|
|
||||||
echo ',' >> data.json
|
|
||||||
echo '${{ toJSON(needs.create-macos-fat-binary.outputs) }}' >> data.json
|
|
||||||
echo ']' >> data.json
|
|
||||||
chmod +x bins/resolc-x86_64-unknown-linux-musl
|
|
||||||
export FIRST_SOLC_VERSION=$(./bins/resolc-x86_64-unknown-linux-musl --supported-solc-versions | cut -f 1 -d "," | tr -d ">=")
|
|
||||||
export LAST_SOLC_VERSION=$(./bins/resolc-x86_64-unknown-linux-musl --supported-solc-versions | cut -f 2 -d "," | tr -d "<=")
|
|
||||||
export FILEPATH=$(readlink -f data.json)
|
|
||||||
export TAG=$(cd revive;gh release list --json name,isLatest --jq '.[] | select(.isLatest)|.name')
|
|
||||||
cd resolc-bin
|
|
||||||
mkdir -p nightly
|
|
||||||
cd nightly
|
|
||||||
python3 ../../revive/.github/scripts/json_generator_nightly.py
|
|
||||||
cd ..
|
|
||||||
git status
|
|
||||||
|
|
||||||
echo "${Green}Add new remote with gh app token${NC}"
|
|
||||||
git remote set-url origin $(git config remote.origin.url | sed "s/github.com/${APP_NAME}:${TOKEN}@github.com/g")
|
|
||||||
|
|
||||||
echo "${Green}Remove http section that causes issues with gh app auth token${NC}"
|
|
||||||
sed -i.bak '/\[http/d' ./.git/config
|
|
||||||
sed -i.bak '/extraheader/d' ./.git/config
|
|
||||||
|
|
||||||
git config user.email "ci@parity.io"
|
|
||||||
git config user.name "${APP_NAME}"
|
|
||||||
|
|
||||||
git add nightly/
|
|
||||||
git commit -m "Update nightly json"
|
|
||||||
git push origin main
|
|
||||||
|
|
||||||
echo "::notice::nightly info.list files were successfully published to https://github.com/paritytech/resolc-bin"
|
|
||||||
@@ -14,7 +14,6 @@ concurrency:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
# if changed, dont forget to update the env var in release-nightly.yml
|
|
||||||
RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067
|
RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
@@ -6,19 +6,6 @@ This is a development pre-release.
|
|||||||
|
|
||||||
Supported `polkadot-sdk` rev: `2503.0.1`
|
Supported `polkadot-sdk` rev: `2503.0.1`
|
||||||
|
|
||||||
## v0.4.0
|
|
||||||
|
|
||||||
This is a development pre-release.
|
|
||||||
|
|
||||||
Supported `polkadot-sdk` rev: `2503.0.1`
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Line debug information per YUL builtin and for `if` statements.
|
|
||||||
- Support for the YUL optimizer details in the standard json input definition.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- The debug info source file matches the YUL path in `--debug-output-dir`, allowing tools to display the source line.
|
|
||||||
|
|
||||||
## v0.3.0
|
## v0.3.0
|
||||||
|
|
||||||
This is a development pre-release.
|
This is a development pre-release.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"Baseline": 945,
|
"Baseline": 939,
|
||||||
"Computation": 2308,
|
"Computation": 2282,
|
||||||
"DivisionArithmetics": 2334,
|
"DivisionArithmetics": 8849,
|
||||||
"ERC20": 21363,
|
"ERC20": 18308,
|
||||||
"Events": 1677,
|
"Events": 1640,
|
||||||
"FibonacciIterative": 1516,
|
"FibonacciIterative": 1497,
|
||||||
"Flipper": 2099,
|
"Flipper": 2099,
|
||||||
"SHA1": 8268
|
"SHA1": 8243
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
pub mod ir_type;
|
pub mod ir_type;
|
||||||
|
|
||||||
use std::path::Path;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@@ -17,14 +16,6 @@ pub struct DebugConfig {
|
|||||||
pub output_directory: Option<PathBuf>,
|
pub output_directory: Option<PathBuf>,
|
||||||
/// Whether debug info should be emitted.
|
/// Whether debug info should be emitted.
|
||||||
pub emit_debug_info: bool,
|
pub emit_debug_info: bool,
|
||||||
/// The YUL debug output file path.
|
|
||||||
///
|
|
||||||
/// Is expected to be configured when running in YUL mode.
|
|
||||||
pub contract_path: Option<PathBuf>,
|
|
||||||
/// The YUL input file path.
|
|
||||||
///
|
|
||||||
/// Is expected to be configured when not running in YUL mode.
|
|
||||||
pub yul_path: Option<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugConfig {
|
impl DebugConfig {
|
||||||
@@ -33,41 +24,15 @@ impl DebugConfig {
|
|||||||
Self {
|
Self {
|
||||||
output_directory,
|
output_directory,
|
||||||
emit_debug_info,
|
emit_debug_info,
|
||||||
contract_path: None,
|
|
||||||
yul_path: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the current YUL path.
|
|
||||||
pub fn set_yul_path(&mut self, yul_path: &Path) {
|
|
||||||
self.yul_path = yul_path.to_path_buf().into();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the current contract path.
|
|
||||||
pub fn set_contract_path(&mut self, contract_path: &str) {
|
|
||||||
self.contract_path = self.yul_source_path(contract_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns with the following precedence:
|
|
||||||
/// 1. The YUL source path if it was configured.
|
|
||||||
/// 2. The source YUL path from the debug output dir if it was configured.
|
|
||||||
/// 3. `None` if there is no debug output directory.
|
|
||||||
pub fn yul_source_path(&self, contract_path: &str) -> Option<PathBuf> {
|
|
||||||
if let Some(path) = self.yul_path.as_ref() {
|
|
||||||
return Some(path.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.output_directory.as_ref().map(|output_directory| {
|
|
||||||
let mut file_path = output_directory.to_owned();
|
|
||||||
let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul);
|
|
||||||
file_path.push(full_file_name);
|
|
||||||
file_path
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dumps the Yul IR.
|
/// Dumps the Yul IR.
|
||||||
pub fn dump_yul(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
|
pub fn dump_yul(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
|
||||||
if let Some(file_path) = self.yul_source_path(contract_path) {
|
if let Some(output_directory) = self.output_directory.as_ref() {
|
||||||
|
let mut file_path = output_directory.to_owned();
|
||||||
|
let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul);
|
||||||
|
file_path.push(full_file_name);
|
||||||
std::fs::write(file_path, code)?;
|
std::fs::write(file_path, code)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,22 @@ pub use self::polkavm::context::function::declaration::Declaration as PolkaVMFun
|
|||||||
pub use self::polkavm::context::function::intrinsics::Intrinsics as PolkaVMIntrinsicFunction;
|
pub use self::polkavm::context::function::intrinsics::Intrinsics as PolkaVMIntrinsicFunction;
|
||||||
pub use self::polkavm::context::function::llvm_runtime::LLVMRuntime as PolkaVMLLVMRuntime;
|
pub use self::polkavm::context::function::llvm_runtime::LLVMRuntime as PolkaVMLLVMRuntime;
|
||||||
pub use self::polkavm::context::function::r#return::Return as PolkaVMFunctionReturn;
|
pub use self::polkavm::context::function::r#return::Return as PolkaVMFunctionReturn;
|
||||||
|
pub use self::polkavm::context::function::runtime::arithmetics::Addition as PolkaVMAdditionFunction;
|
||||||
pub use self::polkavm::context::function::runtime::arithmetics::Division as PolkaVMDivisionFunction;
|
pub use self::polkavm::context::function::runtime::arithmetics::Division as PolkaVMDivisionFunction;
|
||||||
|
pub use self::polkavm::context::function::runtime::arithmetics::Multiplication as PolkaVMMultiplicationFunction;
|
||||||
pub use self::polkavm::context::function::runtime::arithmetics::Remainder as PolkaVMRemainderFunction;
|
pub use self::polkavm::context::function::runtime::arithmetics::Remainder as PolkaVMRemainderFunction;
|
||||||
pub use self::polkavm::context::function::runtime::arithmetics::SignedDivision as PolkaVMSignedDivisionFunction;
|
pub use self::polkavm::context::function::runtime::arithmetics::SignedDivision as PolkaVMSignedDivisionFunction;
|
||||||
pub use self::polkavm::context::function::runtime::arithmetics::SignedRemainder as PolkaVMSignedRemainderFunction;
|
pub use self::polkavm::context::function::runtime::arithmetics::SignedRemainder as PolkaVMSignedRemainderFunction;
|
||||||
|
pub use self::polkavm::context::function::runtime::arithmetics::Subtraction as PolkaVMSubstractionFunction;
|
||||||
|
pub use self::polkavm::context::function::runtime::bitwise::And as PolkaVMAndFunction;
|
||||||
|
pub use self::polkavm::context::function::runtime::bitwise::Byte as PolkaVMByteFunction;
|
||||||
|
pub use self::polkavm::context::function::runtime::bitwise::Or as PolkaVMOrFunction;
|
||||||
|
pub use self::polkavm::context::function::runtime::bitwise::Sar as PolkaVMSarFunction;
|
||||||
|
pub use self::polkavm::context::function::runtime::bitwise::Shl as PolkaVMShlFunction;
|
||||||
|
pub use self::polkavm::context::function::runtime::bitwise::Shr as PolkaVMShrFunction;
|
||||||
|
pub use self::polkavm::context::function::runtime::bitwise::Xor as PolkaVMXorFunction;
|
||||||
|
pub use self::polkavm::context::function::runtime::call::Call as PolkaVMCall;
|
||||||
|
pub use self::polkavm::context::function::runtime::call::CallReentrancyProtector as PolkaVMCallReentrancyProtector;
|
||||||
pub use self::polkavm::context::function::runtime::deploy_code::DeployCode as PolkaVMDeployCodeFunction;
|
pub use self::polkavm::context::function::runtime::deploy_code::DeployCode as PolkaVMDeployCodeFunction;
|
||||||
pub use self::polkavm::context::function::runtime::entry::Entry as PolkaVMEntryFunction;
|
pub use self::polkavm::context::function::runtime::entry::Entry as PolkaVMEntryFunction;
|
||||||
pub use self::polkavm::context::function::runtime::revive::Exit as PolkaVMExitFunction;
|
pub use self::polkavm::context::function::runtime::revive::Exit as PolkaVMExitFunction;
|
||||||
|
|||||||
@@ -51,20 +51,11 @@ pub struct DebugInfo<'ctx> {
|
|||||||
|
|
||||||
impl<'ctx> DebugInfo<'ctx> {
|
impl<'ctx> DebugInfo<'ctx> {
|
||||||
/// A shortcut constructor.
|
/// A shortcut constructor.
|
||||||
pub fn new(
|
pub fn new(module: &inkwell::module::Module<'ctx>) -> Self {
|
||||||
module: &inkwell::module::Module<'ctx>,
|
|
||||||
debug_config: &crate::debug_config::DebugConfig,
|
|
||||||
) -> Self {
|
|
||||||
let module_name = module.get_name().to_string_lossy();
|
|
||||||
let yul_name = debug_config
|
|
||||||
.contract_path
|
|
||||||
.as_ref()
|
|
||||||
.map(|path| path.display().to_string());
|
|
||||||
|
|
||||||
let (builder, compile_unit) = module.create_debug_info_builder(
|
let (builder, compile_unit) = module.create_debug_info_builder(
|
||||||
true,
|
true,
|
||||||
inkwell::debug_info::DWARFSourceLanguage::C,
|
inkwell::debug_info::DWARFSourceLanguage::C,
|
||||||
yul_name.as_deref().unwrap_or_else(|| module_name.as_ref()),
|
module.get_name().to_string_lossy().as_ref(),
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
false,
|
false,
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ use crate::optimizer::settings::size_level::SizeLevel;
|
|||||||
use crate::optimizer::Optimizer;
|
use crate::optimizer::Optimizer;
|
||||||
use crate::polkavm::context::attribute::Attribute;
|
use crate::polkavm::context::attribute::Attribute;
|
||||||
use crate::polkavm::context::pointer::Pointer;
|
use crate::polkavm::context::pointer::Pointer;
|
||||||
use crate::polkavm::context::Context;
|
|
||||||
use crate::polkavm::Dependency;
|
|
||||||
|
|
||||||
use self::declaration::Declaration;
|
use self::declaration::Declaration;
|
||||||
use self::r#return::Return;
|
use self::r#return::Return;
|
||||||
@@ -30,9 +28,7 @@ pub struct Function<'ctx> {
|
|||||||
/// The LLVM function declaration.
|
/// The LLVM function declaration.
|
||||||
declaration: Declaration<'ctx>,
|
declaration: Declaration<'ctx>,
|
||||||
/// The stack representation.
|
/// The stack representation.
|
||||||
stack: HashMap<String, u64>,
|
stack: HashMap<String, Pointer<'ctx>>,
|
||||||
/// The stack variables pointer.
|
|
||||||
stack_variables: Pointer<'ctx>,
|
|
||||||
/// The return value entity.
|
/// The return value entity.
|
||||||
r#return: Return<'ctx>,
|
r#return: Return<'ctx>,
|
||||||
|
|
||||||
@@ -60,14 +56,11 @@ impl<'ctx> Function<'ctx> {
|
|||||||
|
|
||||||
entry_block: inkwell::basic_block::BasicBlock<'ctx>,
|
entry_block: inkwell::basic_block::BasicBlock<'ctx>,
|
||||||
return_block: inkwell::basic_block::BasicBlock<'ctx>,
|
return_block: inkwell::basic_block::BasicBlock<'ctx>,
|
||||||
|
|
||||||
stack_variables: Pointer<'ctx>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
declaration,
|
declaration,
|
||||||
stack: HashMap::with_capacity(Self::STACK_HASHMAP_INITIAL_CAPACITY),
|
stack: HashMap::with_capacity(Self::STACK_HASHMAP_INITIAL_CAPACITY),
|
||||||
stack_variables,
|
|
||||||
r#return,
|
r#return,
|
||||||
|
|
||||||
entry_block,
|
entry_block,
|
||||||
@@ -217,49 +210,22 @@ impl<'ctx> Function<'ctx> {
|
|||||||
|
|
||||||
/// Saves the pointer to a stack variable, returning the pointer to the shadowed variable,
|
/// Saves the pointer to a stack variable, returning the pointer to the shadowed variable,
|
||||||
/// if it exists.
|
/// if it exists.
|
||||||
pub fn insert_stack_pointer<D: Dependency + Clone>(
|
pub fn insert_stack_pointer(
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &mut Context<'ctx, D>,
|
|
||||||
name: String,
|
name: String,
|
||||||
) -> Pointer<'ctx> {
|
pointer: Pointer<'ctx>,
|
||||||
let pointer_name = format!("{}_stack_pointer", &name);
|
) -> Option<Pointer<'ctx>> {
|
||||||
let len = self.stack.len();
|
self.stack.insert(name, pointer)
|
||||||
let index = *self.stack.entry(name).or_insert_with(|| len as u64);
|
|
||||||
let indices = &[
|
|
||||||
context.xlen_type().const_zero(),
|
|
||||||
context.xlen_type().const_int(index, false),
|
|
||||||
];
|
|
||||||
|
|
||||||
context.build_gep(
|
|
||||||
self.stack_variables,
|
|
||||||
indices,
|
|
||||||
context.word_type(),
|
|
||||||
&pointer_name,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the pointer to a stack variable.
|
/// Gets the pointer to a stack variable.
|
||||||
pub fn get_stack_pointer<D: Dependency + Clone>(
|
pub fn get_stack_pointer(&self, name: &str) -> Option<Pointer<'ctx>> {
|
||||||
&self,
|
self.stack.get(name).copied()
|
||||||
context: &mut Context<'ctx, D>,
|
}
|
||||||
name: String,
|
|
||||||
) -> Pointer<'ctx> {
|
|
||||||
let pointer_name = format!("{}_stack_pointer", &name);
|
|
||||||
let index = *self
|
|
||||||
.stack
|
|
||||||
.get(&name)
|
|
||||||
.unwrap_or_else(|| panic!("stack pointer access prior to insertion: {name}"));
|
|
||||||
let indices = &[
|
|
||||||
context.xlen_type().const_zero(),
|
|
||||||
context.xlen_type().const_int(index, false),
|
|
||||||
];
|
|
||||||
|
|
||||||
context.build_gep(
|
/// Removes the pointer to a stack variable.
|
||||||
self.stack_variables,
|
pub fn remove_stack_pointer(&mut self, name: &str) {
|
||||||
indices,
|
self.stack.remove(name);
|
||||||
context.word_type(),
|
|
||||||
&pointer_name,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the return entity representation.
|
/// Returns the return entity representation.
|
||||||
|
|||||||
@@ -7,6 +7,141 @@ use crate::polkavm::context::Context;
|
|||||||
use crate::polkavm::Dependency;
|
use crate::polkavm::Dependency;
|
||||||
use crate::polkavm::WriteLLVM;
|
use crate::polkavm::WriteLLVM;
|
||||||
|
|
||||||
|
/// Implements the ADD operator according to the EVM specification.
|
||||||
|
pub struct Addition;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for Addition
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_addition";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.word_type().fn_type(
|
||||||
|
&[context.word_type().into(), context.word_type().into()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let operand_1 = Self::paramater(context, 0).into_int_value();
|
||||||
|
let operand_2 = Self::paramater(context, 1).into_int_value();
|
||||||
|
|
||||||
|
Ok(Some(
|
||||||
|
context
|
||||||
|
.builder()
|
||||||
|
.build_int_add(operand_1, operand_2, "ADD")
|
||||||
|
.map(Into::into)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for Addition
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the SUB operator according to the EVM specification.
|
||||||
|
pub struct Subtraction;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for Subtraction
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_subtraction";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.word_type().fn_type(
|
||||||
|
&[context.word_type().into(), context.word_type().into()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let operand_1 = Self::paramater(context, 0).into_int_value();
|
||||||
|
let operand_2 = Self::paramater(context, 1).into_int_value();
|
||||||
|
|
||||||
|
Ok(Some(
|
||||||
|
context
|
||||||
|
.builder()
|
||||||
|
.build_int_sub(operand_1, operand_2, "SUB")
|
||||||
|
.map(Into::into)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for Subtraction
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the MUL operator according to the EVM specification.
|
||||||
|
pub struct Multiplication;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for Multiplication
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_multiplication";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.word_type().fn_type(
|
||||||
|
&[context.word_type().into(), context.word_type().into()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let operand_1 = Self::paramater(context, 0).into_int_value();
|
||||||
|
let operand_2 = Self::paramater(context, 1).into_int_value();
|
||||||
|
|
||||||
|
Ok(Some(
|
||||||
|
context
|
||||||
|
.builder()
|
||||||
|
.build_int_mul(operand_1, operand_2, "MUL")
|
||||||
|
.map(Into::into)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for Multiplication
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Implements the division operator according to the EVM specification.
|
/// Implements the division operator according to the EVM specification.
|
||||||
pub struct Division;
|
pub struct Division;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,494 @@
|
|||||||
|
//! Translates the arithmetic operations.
|
||||||
|
|
||||||
|
use inkwell::values::BasicValue;
|
||||||
|
|
||||||
|
use crate::polkavm::context::runtime::RuntimeFunction;
|
||||||
|
use crate::polkavm::context::Context;
|
||||||
|
use crate::polkavm::Dependency;
|
||||||
|
use crate::polkavm::WriteLLVM;
|
||||||
|
|
||||||
|
/// Implements the OR operator according to the EVM specification.
|
||||||
|
pub struct Or;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for Or
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_or";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.word_type().fn_type(
|
||||||
|
&[context.word_type().into(), context.word_type().into()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let operand_1 = Self::paramater(context, 0).into_int_value();
|
||||||
|
let operand_2 = Self::paramater(context, 1).into_int_value();
|
||||||
|
|
||||||
|
Ok(Some(
|
||||||
|
context
|
||||||
|
.builder()
|
||||||
|
.build_or(operand_1, operand_2, "OR")
|
||||||
|
.map(Into::into)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for Or
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the XOR operator according to the EVM specification.
|
||||||
|
pub struct Xor;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for Xor
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_xor";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.word_type().fn_type(
|
||||||
|
&[context.word_type().into(), context.word_type().into()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let operand_1 = Self::paramater(context, 0).into_int_value();
|
||||||
|
let operand_2 = Self::paramater(context, 1).into_int_value();
|
||||||
|
|
||||||
|
Ok(Some(
|
||||||
|
context
|
||||||
|
.builder()
|
||||||
|
.build_xor(operand_1, operand_2, "XOR")
|
||||||
|
.map(Into::into)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for Xor
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the AND operator according to the EVM specification.
|
||||||
|
pub struct And;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for And
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_and";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.word_type().fn_type(
|
||||||
|
&[context.word_type().into(), context.word_type().into()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let operand_1 = Self::paramater(context, 0).into_int_value();
|
||||||
|
let operand_2 = Self::paramater(context, 1).into_int_value();
|
||||||
|
|
||||||
|
Ok(Some(
|
||||||
|
context
|
||||||
|
.builder()
|
||||||
|
.build_and(operand_1, operand_2, "AND")
|
||||||
|
.map(Into::into)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for And
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Implements the SHL operator according to the EVM specification.
|
||||||
|
pub struct Shl;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for Shl
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_shl";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.word_type().fn_type(
|
||||||
|
&[context.word_type().into(), context.word_type().into()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let shift = Self::paramater(context, 0).into_int_value();
|
||||||
|
let value = Self::paramater(context, 1).into_int_value();
|
||||||
|
|
||||||
|
let overflow_block = context.append_basic_block("shift_left_overflow");
|
||||||
|
let non_overflow_block = context.append_basic_block("shift_left_non_overflow");
|
||||||
|
let join_block = context.append_basic_block("shift_left_join");
|
||||||
|
|
||||||
|
let condition_is_overflow = context.builder().build_int_compare(
|
||||||
|
inkwell::IntPredicate::UGT,
|
||||||
|
shift,
|
||||||
|
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
|
||||||
|
"shift_left_is_overflow",
|
||||||
|
)?;
|
||||||
|
context.build_conditional_branch(
|
||||||
|
condition_is_overflow,
|
||||||
|
overflow_block,
|
||||||
|
non_overflow_block,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
context.set_basic_block(overflow_block);
|
||||||
|
context.build_unconditional_branch(join_block);
|
||||||
|
|
||||||
|
context.set_basic_block(non_overflow_block);
|
||||||
|
let value =
|
||||||
|
context
|
||||||
|
.builder()
|
||||||
|
.build_left_shift(value, shift, "shift_left_non_overflow_result")?;
|
||||||
|
context.build_unconditional_branch(join_block);
|
||||||
|
|
||||||
|
context.set_basic_block(join_block);
|
||||||
|
let result = context
|
||||||
|
.builder()
|
||||||
|
.build_phi(context.word_type(), "shift_left_value")?;
|
||||||
|
result.add_incoming(&[
|
||||||
|
(&value, non_overflow_block),
|
||||||
|
(&context.word_const(0), overflow_block),
|
||||||
|
]);
|
||||||
|
Ok(Some(result.as_basic_value()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for Shl
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the SHR operator according to the EVM specification.
|
||||||
|
pub struct Shr;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for Shr
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_shr";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.word_type().fn_type(
|
||||||
|
&[context.word_type().into(), context.word_type().into()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let shift = Self::paramater(context, 0).into_int_value();
|
||||||
|
let value = Self::paramater(context, 1).into_int_value();
|
||||||
|
|
||||||
|
let overflow_block = context.append_basic_block("shift_right_overflow");
|
||||||
|
let non_overflow_block = context.append_basic_block("shift_right_non_overflow");
|
||||||
|
let join_block = context.append_basic_block("shift_right_join");
|
||||||
|
|
||||||
|
let condition_is_overflow = context.builder().build_int_compare(
|
||||||
|
inkwell::IntPredicate::UGT,
|
||||||
|
shift,
|
||||||
|
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
|
||||||
|
"shift_right_is_overflow",
|
||||||
|
)?;
|
||||||
|
context.build_conditional_branch(
|
||||||
|
condition_is_overflow,
|
||||||
|
overflow_block,
|
||||||
|
non_overflow_block,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
context.set_basic_block(overflow_block);
|
||||||
|
context.build_unconditional_branch(join_block);
|
||||||
|
|
||||||
|
context.set_basic_block(non_overflow_block);
|
||||||
|
let value = context.builder().build_right_shift(
|
||||||
|
value,
|
||||||
|
shift,
|
||||||
|
false,
|
||||||
|
"shift_right_non_overflow_result",
|
||||||
|
)?;
|
||||||
|
context.build_unconditional_branch(join_block);
|
||||||
|
|
||||||
|
context.set_basic_block(join_block);
|
||||||
|
let result = context
|
||||||
|
.builder()
|
||||||
|
.build_phi(context.word_type(), "shift_right_value")?;
|
||||||
|
result.add_incoming(&[
|
||||||
|
(&value, non_overflow_block),
|
||||||
|
(&context.word_const(0), overflow_block),
|
||||||
|
]);
|
||||||
|
Ok(Some(result.as_basic_value()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for Shr
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the SAR operator according to the EVM specification.
|
||||||
|
pub struct Sar;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for Sar
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_sar";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.word_type().fn_type(
|
||||||
|
&[context.word_type().into(), context.word_type().into()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let shift = Self::paramater(context, 0).into_int_value();
|
||||||
|
let value = Self::paramater(context, 1).into_int_value();
|
||||||
|
|
||||||
|
let overflow_block = context.append_basic_block("shift_right_arithmetic_overflow");
|
||||||
|
let overflow_positive_block =
|
||||||
|
context.append_basic_block("shift_right_arithmetic_overflow_positive");
|
||||||
|
let overflow_negative_block =
|
||||||
|
context.append_basic_block("shift_right_arithmetic_overflow_negative");
|
||||||
|
let non_overflow_block = context.append_basic_block("shift_right_arithmetic_non_overflow");
|
||||||
|
let join_block = context.append_basic_block("shift_right_arithmetic_join");
|
||||||
|
|
||||||
|
let condition_is_overflow = context.builder().build_int_compare(
|
||||||
|
inkwell::IntPredicate::UGT,
|
||||||
|
shift,
|
||||||
|
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
|
||||||
|
"shift_right_arithmetic_is_overflow",
|
||||||
|
)?;
|
||||||
|
context.build_conditional_branch(
|
||||||
|
condition_is_overflow,
|
||||||
|
overflow_block,
|
||||||
|
non_overflow_block,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
context.set_basic_block(overflow_block);
|
||||||
|
let sign_bit = context.builder().build_right_shift(
|
||||||
|
value,
|
||||||
|
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
|
||||||
|
false,
|
||||||
|
"shift_right_arithmetic_sign_bit",
|
||||||
|
)?;
|
||||||
|
let condition_is_negative = context.builder().build_int_truncate_or_bit_cast(
|
||||||
|
sign_bit,
|
||||||
|
context.bool_type(),
|
||||||
|
"shift_right_arithmetic_sign_bit_truncated",
|
||||||
|
)?;
|
||||||
|
context.build_conditional_branch(
|
||||||
|
condition_is_negative,
|
||||||
|
overflow_negative_block,
|
||||||
|
overflow_positive_block,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
context.set_basic_block(overflow_positive_block);
|
||||||
|
context.build_unconditional_branch(join_block);
|
||||||
|
|
||||||
|
context.set_basic_block(overflow_negative_block);
|
||||||
|
context.build_unconditional_branch(join_block);
|
||||||
|
|
||||||
|
context.set_basic_block(non_overflow_block);
|
||||||
|
let value = context.builder().build_right_shift(
|
||||||
|
value,
|
||||||
|
shift,
|
||||||
|
true,
|
||||||
|
"shift_right_arithmetic_non_overflow_result",
|
||||||
|
)?;
|
||||||
|
context.build_unconditional_branch(join_block);
|
||||||
|
|
||||||
|
context.set_basic_block(join_block);
|
||||||
|
let result = context
|
||||||
|
.builder()
|
||||||
|
.build_phi(context.word_type(), "shift_arithmetic_right_value")?;
|
||||||
|
result.add_incoming(&[
|
||||||
|
(&value, non_overflow_block),
|
||||||
|
(
|
||||||
|
&context.word_type().const_all_ones(),
|
||||||
|
overflow_negative_block,
|
||||||
|
),
|
||||||
|
(&context.word_const(0), overflow_positive_block),
|
||||||
|
]);
|
||||||
|
Ok(Some(result.as_basic_value()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for Sar
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Implements the BYTE operator according to the EVM specification.
|
||||||
|
pub struct Byte;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for Byte
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_byte";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.word_type().fn_type(
|
||||||
|
&[context.word_type().into(), context.word_type().into()],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let operand_1 = Self::paramater(context, 0).into_int_value();
|
||||||
|
let operand_2 = Self::paramater(context, 1).into_int_value();
|
||||||
|
const MAX_INDEX_BYTES: u64 = 31;
|
||||||
|
|
||||||
|
let is_overflow_bit = context.builder().build_int_compare(
|
||||||
|
inkwell::IntPredicate::ULE,
|
||||||
|
operand_1,
|
||||||
|
context.word_const(MAX_INDEX_BYTES),
|
||||||
|
"is_overflow_bit",
|
||||||
|
)?;
|
||||||
|
let is_overflow_byte = context.builder().build_int_z_extend(
|
||||||
|
is_overflow_bit,
|
||||||
|
context.byte_type(),
|
||||||
|
"is_overflow_byte",
|
||||||
|
)?;
|
||||||
|
let mask_byte = context.builder().build_int_mul(
|
||||||
|
context.byte_type().const_all_ones(),
|
||||||
|
is_overflow_byte,
|
||||||
|
"mask_byte",
|
||||||
|
)?;
|
||||||
|
let mask_byte_word = context.builder().build_int_z_extend(
|
||||||
|
mask_byte,
|
||||||
|
context.word_type(),
|
||||||
|
"mask_byte_word",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let index_truncated = context.builder().build_int_truncate(
|
||||||
|
operand_1,
|
||||||
|
context.byte_type(),
|
||||||
|
"index_truncated",
|
||||||
|
)?;
|
||||||
|
let index_in_bits = context.builder().build_int_mul(
|
||||||
|
index_truncated,
|
||||||
|
context
|
||||||
|
.byte_type()
|
||||||
|
.const_int(revive_common::BIT_LENGTH_BYTE as u64, false),
|
||||||
|
"index_in_bits",
|
||||||
|
)?;
|
||||||
|
let index_from_most_significant_bit = context.builder().build_int_sub(
|
||||||
|
context.byte_type().const_int(
|
||||||
|
MAX_INDEX_BYTES * revive_common::BIT_LENGTH_BYTE as u64,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
index_in_bits,
|
||||||
|
"index_from_msb",
|
||||||
|
)?;
|
||||||
|
let index_extended = context.builder().build_int_z_extend(
|
||||||
|
index_from_most_significant_bit,
|
||||||
|
context.word_type(),
|
||||||
|
"index",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mask = context
|
||||||
|
.builder()
|
||||||
|
.build_left_shift(mask_byte_word, index_extended, "mask")?;
|
||||||
|
let masked_value = context.builder().build_and(operand_2, mask, "masked")?;
|
||||||
|
let byte =
|
||||||
|
context
|
||||||
|
.builder()
|
||||||
|
.build_right_shift(masked_value, index_extended, false, "byte")?;
|
||||||
|
|
||||||
|
Ok(Some(byte.as_basic_value_enum()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for Byte
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
//! Translates the arithmetic operations.
|
||||||
|
|
||||||
|
use inkwell::values::BasicValue;
|
||||||
|
|
||||||
|
use crate::polkavm::context::runtime::RuntimeFunction;
|
||||||
|
use crate::polkavm::context::Context;
|
||||||
|
use crate::polkavm::context::Pointer;
|
||||||
|
use crate::polkavm::Dependency;
|
||||||
|
use crate::polkavm::WriteLLVM;
|
||||||
|
|
||||||
|
const SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD: u64 = 2300;
|
||||||
|
|
||||||
|
/// The Solidity `address.transfer` and `address.send` call detection heuristic.
|
||||||
|
///
|
||||||
|
/// # Why
|
||||||
|
/// This heuristic is an additional security feature to guard against re-entrancy attacks
|
||||||
|
/// in case contract authors violate Solidity best practices and use `address.transfer` or
|
||||||
|
/// `address.send`.
|
||||||
|
/// While contract authors are supposed to never use `address.transfer` or `address.send`,
|
||||||
|
/// for a small cost we can be extra defensive about it.
|
||||||
|
///
|
||||||
|
/// # How
|
||||||
|
/// The gas stipend emitted by solc for `transfer` and `send` is not static, thus:
|
||||||
|
/// - Dynamically allow re-entrancy only for calls considered not transfer or send.
|
||||||
|
/// - Detected balance transfers will supply 0 deposit limit instead of `u256::MAX`.
|
||||||
|
///
|
||||||
|
/// Calls are considered transfer or send if:
|
||||||
|
/// - (Input length | Output lenght) == 0;
|
||||||
|
/// - Gas <= 2300;
|
||||||
|
///
|
||||||
|
/// # Arguments:
|
||||||
|
/// - The deposit value pointer.
|
||||||
|
/// - The gas value.
|
||||||
|
/// - `input_length | output_length`.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// # Returns:
|
||||||
|
/// The call flags xlen `IntValue`
|
||||||
|
pub struct CallReentrancyProtector;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for CallReentrancyProtector
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_call_reentrancy_protector";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.xlen_type().fn_type(
|
||||||
|
&[
|
||||||
|
context.llvm().ptr_type(Default::default()).into(),
|
||||||
|
context.word_type().into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let deposit_pointer = Self::paramater(context, 0).into_pointer_value();
|
||||||
|
let gas = Self::paramater(context, 1).into_int_value();
|
||||||
|
let input_length_or_output_length = Self::paramater(context, 2).into_int_value();
|
||||||
|
|
||||||
|
// Branch-free SSA implementation: First derive the heuristic boolean (int1) value.
|
||||||
|
let is_no_input_no_output = context.builder().build_int_compare(
|
||||||
|
inkwell::IntPredicate::EQ,
|
||||||
|
context.xlen_type().const_zero(),
|
||||||
|
input_length_or_output_length,
|
||||||
|
"is_no_input_no_output",
|
||||||
|
)?;
|
||||||
|
let gas_stipend = context
|
||||||
|
.word_type()
|
||||||
|
.const_int(SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD, false);
|
||||||
|
let is_gas_stipend_for_transfer_or_send = context.builder().build_int_compare(
|
||||||
|
inkwell::IntPredicate::ULE,
|
||||||
|
gas,
|
||||||
|
gas_stipend,
|
||||||
|
"is_gas_stipend_for_transfer_or_send",
|
||||||
|
)?;
|
||||||
|
let is_balance_transfer = context.builder().build_and(
|
||||||
|
is_no_input_no_output,
|
||||||
|
is_gas_stipend_for_transfer_or_send,
|
||||||
|
"is_balance_transfer",
|
||||||
|
)?;
|
||||||
|
let is_regular_call = context
|
||||||
|
.builder()
|
||||||
|
.build_not(is_balance_transfer, "is_balance_transfer_inverted")?;
|
||||||
|
|
||||||
|
// Call flag: Left shift the heuristic boolean value.
|
||||||
|
let is_regular_call_xlen = context.builder().build_int_z_extend(
|
||||||
|
is_regular_call,
|
||||||
|
context.xlen_type(),
|
||||||
|
"is_balance_transfer_xlen",
|
||||||
|
)?;
|
||||||
|
let call_flags = context.builder().build_left_shift(
|
||||||
|
is_regular_call_xlen,
|
||||||
|
context.xlen_type().const_int(3, false),
|
||||||
|
"flags",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Deposit limit value: Sign-extended the heuristic boolean value.
|
||||||
|
let deposit_limit_value = context.builder().build_int_s_extend(
|
||||||
|
is_regular_call,
|
||||||
|
context.word_type(),
|
||||||
|
"deposit_limit_value",
|
||||||
|
)?;
|
||||||
|
context
|
||||||
|
.builder()
|
||||||
|
.build_store(deposit_pointer, deposit_limit_value)?;
|
||||||
|
|
||||||
|
Ok(Some(call_flags.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for CallReentrancyProtector
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the CALL operator according to the EVM specification.
|
||||||
|
///
|
||||||
|
/// # Arguments:
|
||||||
|
/// - The address value.
|
||||||
|
/// - The value value.
|
||||||
|
/// - The input offset.
|
||||||
|
/// - The input length.
|
||||||
|
/// - The output offset.
|
||||||
|
/// - The output length.
|
||||||
|
/// - The deposit limit pointer.
|
||||||
|
/// - The call flags.
|
||||||
|
///
|
||||||
|
/// # Returns:
|
||||||
|
/// - The success value (as xlen)
|
||||||
|
pub struct Call;
|
||||||
|
|
||||||
|
impl<D> RuntimeFunction<D> for Call
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
const NAME: &'static str = "__revive_call";
|
||||||
|
|
||||||
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx> {
|
||||||
|
context.register_type().fn_type(
|
||||||
|
&[
|
||||||
|
context.word_type().into(),
|
||||||
|
context.word_type().into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
context.llvm().ptr_type(Default::default()).into(),
|
||||||
|
context.xlen_type().into(),
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_body<'ctx>(
|
||||||
|
&self,
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>> {
|
||||||
|
let address = Self::paramater(context, 0).into_int_value();
|
||||||
|
let value = Self::paramater(context, 1).into_int_value();
|
||||||
|
let input_offset = Self::paramater(context, 2).into_int_value();
|
||||||
|
let input_length = Self::paramater(context, 3).into_int_value();
|
||||||
|
let output_offset = Self::paramater(context, 4).into_int_value();
|
||||||
|
let output_length = Self::paramater(context, 5).into_int_value();
|
||||||
|
let depsit_limit_pointer = Self::paramater(context, 6).into_pointer_value();
|
||||||
|
let flags = Self::paramater(context, 7).into_int_value();
|
||||||
|
|
||||||
|
let address_pointer = context.build_address_argument_store(address)?;
|
||||||
|
|
||||||
|
let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer");
|
||||||
|
context.build_store(value_pointer, value)?;
|
||||||
|
|
||||||
|
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
|
||||||
|
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
|
||||||
|
|
||||||
|
let output_length_pointer =
|
||||||
|
context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
||||||
|
context.build_store(output_length_pointer, output_length)?;
|
||||||
|
|
||||||
|
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
|
context.builder(),
|
||||||
|
context.llvm(),
|
||||||
|
flags,
|
||||||
|
address_pointer.to_int(context),
|
||||||
|
"address_and_callee",
|
||||||
|
)?;
|
||||||
|
let deposit_limit_pointer = Pointer::new(
|
||||||
|
context.word_type(),
|
||||||
|
Default::default(),
|
||||||
|
depsit_limit_pointer,
|
||||||
|
);
|
||||||
|
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
|
context.builder(),
|
||||||
|
context.llvm(),
|
||||||
|
deposit_limit_pointer.to_int(context),
|
||||||
|
value_pointer.to_int(context),
|
||||||
|
"deposit_and_value",
|
||||||
|
)?;
|
||||||
|
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
|
context.builder(),
|
||||||
|
context.llvm(),
|
||||||
|
input_length,
|
||||||
|
input_pointer.to_int(context),
|
||||||
|
"input_data",
|
||||||
|
)?;
|
||||||
|
let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
|
context.builder(),
|
||||||
|
context.llvm(),
|
||||||
|
output_length_pointer.to_int(context),
|
||||||
|
output_pointer.to_int(context),
|
||||||
|
"output_data",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let name = revive_runtime_api::polkavm_imports::CALL;
|
||||||
|
let success = context
|
||||||
|
.build_runtime_call(
|
||||||
|
name,
|
||||||
|
&[
|
||||||
|
flags_and_callee.into(),
|
||||||
|
context.register_type().const_all_ones().into(),
|
||||||
|
context.register_type().const_all_ones().into(),
|
||||||
|
deposit_and_value.into(),
|
||||||
|
input_data.into(),
|
||||||
|
output_data.into(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|| panic!("{name} should return a value"))
|
||||||
|
.into_int_value();
|
||||||
|
Ok(Some(success.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> WriteLLVM<D> for Call
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::declare(self, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||||
|
<Self as RuntimeFunction<_>>::emit(&self, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,7 +48,6 @@ where
|
|||||||
function_type,
|
function_type,
|
||||||
0,
|
0,
|
||||||
Some(inkwell::module::Linkage::External),
|
Some(inkwell::module::Linkage::External),
|
||||||
1024,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.inner.declare(context)
|
self.inner.declare(context)
|
||||||
|
|||||||
@@ -145,7 +145,6 @@ where
|
|||||||
entry_function_type,
|
entry_function_type,
|
||||||
0,
|
0,
|
||||||
Some(inkwell::module::Linkage::External),
|
Some(inkwell::module::Linkage::External),
|
||||||
0,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
context.declare_global(
|
context.declare_global(
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
//! The front-end runtime functions.
|
//! The front-end runtime functions.
|
||||||
|
|
||||||
pub mod arithmetics;
|
pub mod arithmetics;
|
||||||
|
pub mod bitwise;
|
||||||
|
pub mod call;
|
||||||
pub mod deploy_code;
|
pub mod deploy_code;
|
||||||
pub mod entry;
|
pub mod entry;
|
||||||
pub mod revive;
|
pub mod revive;
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ where
|
|||||||
function_type,
|
function_type,
|
||||||
0,
|
0,
|
||||||
Some(inkwell::module::Linkage::External),
|
Some(inkwell::module::Linkage::External),
|
||||||
1024,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.inner.declare(context)
|
self.inner.declare(context)
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ where
|
|||||||
let intrinsics = Intrinsics::new(llvm, &module);
|
let intrinsics = Intrinsics::new(llvm, &module);
|
||||||
let llvm_runtime = LLVMRuntime::new(llvm, &module, &optimizer);
|
let llvm_runtime = LLVMRuntime::new(llvm, &module, &optimizer);
|
||||||
let debug_info = debug_config.emit_debug_info.then(|| {
|
let debug_info = debug_config.emit_debug_info.then(|| {
|
||||||
let debug_info = DebugInfo::new(&module, &debug_config);
|
let debug_info = DebugInfo::new(&module);
|
||||||
debug_info.initialize_module(llvm, &module);
|
debug_info.initialize_module(llvm, &module);
|
||||||
debug_info
|
debug_info
|
||||||
});
|
});
|
||||||
@@ -463,7 +463,6 @@ where
|
|||||||
r#type: inkwell::types::FunctionType<'ctx>,
|
r#type: inkwell::types::FunctionType<'ctx>,
|
||||||
return_values_length: usize,
|
return_values_length: usize,
|
||||||
linkage: Option<inkwell::module::Linkage>,
|
linkage: Option<inkwell::module::Linkage>,
|
||||||
stack_variables: u32,
|
|
||||||
) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> {
|
) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> {
|
||||||
let value = self.module().add_function(name, r#type, linkage);
|
let value = self.module().add_function(name, r#type, linkage);
|
||||||
|
|
||||||
@@ -485,30 +484,15 @@ where
|
|||||||
let entry_block = self.llvm.append_basic_block(value, "entry");
|
let entry_block = self.llvm.append_basic_block(value, "entry");
|
||||||
let return_block = self.llvm.append_basic_block(value, "return");
|
let return_block = self.llvm.append_basic_block(value, "return");
|
||||||
|
|
||||||
self.builder().position_at_end(entry_block);
|
|
||||||
let stack_variables_pointer = self.build_alloca(
|
|
||||||
self.word_type().array_type(stack_variables),
|
|
||||||
"stack_variables",
|
|
||||||
);
|
|
||||||
|
|
||||||
let r#return = match return_values_length {
|
let r#return = match return_values_length {
|
||||||
0 => FunctionReturn::none(),
|
0 => FunctionReturn::none(),
|
||||||
1 => {
|
1 => {
|
||||||
//self.set_basic_block(entry_block);
|
self.set_basic_block(entry_block);
|
||||||
//let pointer = self
|
let pointer = self.build_alloca(self.word_type(), "return_pointer");
|
||||||
// .current_function()
|
FunctionReturn::primitive(pointer)
|
||||||
// .borrow_mut()
|
|
||||||
// .insert_stack_pointer(self, format!("{name}_return_pointer"));
|
|
||||||
|
|
||||||
FunctionReturn::primitive(self.build_gep(
|
|
||||||
stack_variables_pointer,
|
|
||||||
&[self.xlen_type().const_zero(), self.xlen_type().const_zero()],
|
|
||||||
self.word_type(),
|
|
||||||
"return_pointer",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
size => {
|
size => {
|
||||||
//self.set_basic_block(entry_block);
|
self.set_basic_block(entry_block);
|
||||||
let pointer = self.build_alloca(
|
let pointer = self.build_alloca(
|
||||||
self.structure_type(
|
self.structure_type(
|
||||||
vec![self.word_type().as_basic_type_enum(); size].as_slice(),
|
vec![self.word_type().as_basic_type_enum(); size].as_slice(),
|
||||||
@@ -525,7 +509,6 @@ where
|
|||||||
r#return,
|
r#return,
|
||||||
entry_block,
|
entry_block,
|
||||||
return_block,
|
return_block,
|
||||||
stack_variables_pointer,
|
|
||||||
);
|
);
|
||||||
Function::set_default_attributes(self.llvm, function.declaration(), &self.optimizer);
|
Function::set_default_attributes(self.llvm, function.declaration(), &self.optimizer);
|
||||||
let function = Rc::new(RefCell::new(function));
|
let function = Rc::new(RefCell::new(function));
|
||||||
@@ -753,23 +736,6 @@ where
|
|||||||
pointer
|
pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn variable_decl<T: BasicType<'ctx> + Clone + Copy>(
|
|
||||||
&self,
|
|
||||||
r#type: T,
|
|
||||||
name: &str,
|
|
||||||
) -> Pointer<'ctx> {
|
|
||||||
let pointer = self.builder.build_alloca(r#type, name).unwrap();
|
|
||||||
|
|
||||||
pointer
|
|
||||||
.as_instruction()
|
|
||||||
.unwrap()
|
|
||||||
.set_alignment(revive_common::BYTE_LENGTH_STACK_ALIGN as u32)
|
|
||||||
.expect("Alignment is valid");
|
|
||||||
|
|
||||||
Pointer::new(r#type, AddressSpace::Stack, pointer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Truncate `address` to the ethereum address length and store it as bytes on the stack.
|
|
||||||
/// Builds an aligned stack allocation at the current position.
|
/// Builds an aligned stack allocation at the current position.
|
||||||
/// Use this if [`build_alloca_at_entry`] might change program semantics.
|
/// Use this if [`build_alloca_at_entry`] might change program semantics.
|
||||||
/// Otherwise, alloca should always be built at the function prelude!
|
/// Otherwise, alloca should always be built at the function prelude!
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ where
|
|||||||
Self::r#type(context),
|
Self::r#type(context),
|
||||||
0,
|
0,
|
||||||
Some(inkwell::module::Linkage::External),
|
Some(inkwell::module::Linkage::External),
|
||||||
0,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut attributes = Self::ATTRIBUTES.to_vec();
|
let mut attributes = Self::ATTRIBUTES.to_vec();
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ pub fn check_attribute_null_pointer_is_invalid() {
|
|||||||
.fn_type(&[context.word_type().into()], false),
|
.fn_type(&[context.word_type().into()], false),
|
||||||
1,
|
1,
|
||||||
Some(inkwell::module::Linkage::External),
|
Some(inkwell::module::Linkage::External),
|
||||||
16,
|
|
||||||
)
|
)
|
||||||
.expect("Failed to add function");
|
.expect("Failed to add function");
|
||||||
assert!(!function
|
assert!(!function
|
||||||
@@ -64,7 +63,6 @@ pub fn check_attribute_optimize_for_size_mode_3() {
|
|||||||
.fn_type(&[context.word_type().into()], false),
|
.fn_type(&[context.word_type().into()], false),
|
||||||
1,
|
1,
|
||||||
Some(inkwell::module::Linkage::External),
|
Some(inkwell::module::Linkage::External),
|
||||||
16,
|
|
||||||
)
|
)
|
||||||
.expect("Failed to add function");
|
.expect("Failed to add function");
|
||||||
assert!(!function
|
assert!(!function
|
||||||
@@ -88,7 +86,6 @@ pub fn check_attribute_optimize_for_size_mode_z() {
|
|||||||
.fn_type(&[context.word_type().into()], false),
|
.fn_type(&[context.word_type().into()], false),
|
||||||
1,
|
1,
|
||||||
Some(inkwell::module::Linkage::External),
|
Some(inkwell::module::Linkage::External),
|
||||||
16,
|
|
||||||
)
|
)
|
||||||
.expect("Failed to add function");
|
.expect("Failed to add function");
|
||||||
assert!(function
|
assert!(function
|
||||||
@@ -112,7 +109,6 @@ pub fn check_attribute_min_size_mode_3() {
|
|||||||
.fn_type(&[context.word_type().into()], false),
|
.fn_type(&[context.word_type().into()], false),
|
||||||
1,
|
1,
|
||||||
Some(inkwell::module::Linkage::External),
|
Some(inkwell::module::Linkage::External),
|
||||||
16,
|
|
||||||
)
|
)
|
||||||
.expect("Failed to add function");
|
.expect("Failed to add function");
|
||||||
assert!(!function
|
assert!(!function
|
||||||
@@ -136,7 +132,6 @@ pub fn check_attribute_min_size_mode_z() {
|
|||||||
.fn_type(&[context.word_type().into()], false),
|
.fn_type(&[context.word_type().into()], false),
|
||||||
1,
|
1,
|
||||||
Some(inkwell::module::Linkage::External),
|
Some(inkwell::module::Linkage::External),
|
||||||
16,
|
|
||||||
)
|
)
|
||||||
.expect("Failed to add function");
|
.expect("Failed to add function");
|
||||||
assert!(function
|
assert!(function
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
//! Translates the arithmetic operations.
|
//! Translates the arithmetic operations.
|
||||||
|
|
||||||
use inkwell::values::BasicValue;
|
|
||||||
|
|
||||||
use crate::polkavm::context::runtime::RuntimeFunction;
|
use crate::polkavm::context::runtime::RuntimeFunction;
|
||||||
use crate::polkavm::context::Context;
|
use crate::polkavm::context::Context;
|
||||||
use crate::polkavm::Dependency;
|
use crate::polkavm::Dependency;
|
||||||
|
use crate::PolkaVMAdditionFunction;
|
||||||
use crate::PolkaVMDivisionFunction;
|
use crate::PolkaVMDivisionFunction;
|
||||||
|
use crate::PolkaVMMultiplicationFunction;
|
||||||
use crate::PolkaVMRemainderFunction;
|
use crate::PolkaVMRemainderFunction;
|
||||||
use crate::PolkaVMSignedDivisionFunction;
|
use crate::PolkaVMSignedDivisionFunction;
|
||||||
use crate::PolkaVMSignedRemainderFunction;
|
use crate::PolkaVMSignedRemainderFunction;
|
||||||
|
use crate::PolkaVMSubstractionFunction;
|
||||||
|
|
||||||
/// Translates the arithmetic addition.
|
/// Translates the arithmetic addition.
|
||||||
pub fn addition<'ctx, D>(
|
pub fn addition<'ctx, D>(
|
||||||
@@ -19,10 +20,11 @@ pub fn addition<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMAdditionFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMAdditionFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "SUB")
|
||||||
.build_int_add(operand_1, operand_2, "addition_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the arithmetic subtraction.
|
/// Translates the arithmetic subtraction.
|
||||||
@@ -34,10 +36,11 @@ pub fn subtraction<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMSubstractionFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMSubstractionFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "SUB")
|
||||||
.build_int_sub(operand_1, operand_2, "subtraction_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the arithmetic multiplication.
|
/// Translates the arithmetic multiplication.
|
||||||
@@ -49,10 +52,11 @@ pub fn multiplication<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMMultiplicationFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMMultiplicationFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "MUL")
|
||||||
.build_int_mul(operand_1, operand_2, "multiplication_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the arithmetic division.
|
/// Translates the arithmetic division.
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
//! Translates the bitwise operations.
|
//! Translates the bitwise operations.
|
||||||
|
|
||||||
use inkwell::values::BasicValue;
|
use crate::polkavm::context::runtime::RuntimeFunction;
|
||||||
|
|
||||||
use crate::polkavm::context::Context;
|
use crate::polkavm::context::Context;
|
||||||
use crate::polkavm::Dependency;
|
use crate::polkavm::Dependency;
|
||||||
|
use crate::{
|
||||||
|
PolkaVMAndFunction, PolkaVMByteFunction, PolkaVMOrFunction, PolkaVMSarFunction,
|
||||||
|
PolkaVMShlFunction, PolkaVMShrFunction, PolkaVMXorFunction,
|
||||||
|
};
|
||||||
|
|
||||||
/// Translates the bitwise OR.
|
/// Translates the bitwise OR.
|
||||||
pub fn or<'ctx, D>(
|
pub fn or<'ctx, D>(
|
||||||
@@ -14,10 +17,11 @@ pub fn or<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMOrFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMOrFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "OR")
|
||||||
.build_or(operand_1, operand_2, "or_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the bitwise XOR.
|
/// Translates the bitwise XOR.
|
||||||
@@ -29,10 +33,11 @@ pub fn xor<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMXorFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMXorFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "XOR")
|
||||||
.build_xor(operand_1, operand_2, "xor_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the bitwise AND.
|
/// Translates the bitwise AND.
|
||||||
@@ -44,10 +49,11 @@ pub fn and<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
|
let name = <PolkaVMAndFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMAndFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
Ok(context
|
Ok(context
|
||||||
.builder()
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "AND")
|
||||||
.build_and(operand_1, operand_2, "and_result")?
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the bitwise shift left.
|
/// Translates the bitwise shift left.
|
||||||
@@ -59,37 +65,11 @@ pub fn shift_left<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
let overflow_block = context.append_basic_block("shift_left_overflow");
|
let name = <PolkaVMShlFunction as RuntimeFunction<D>>::NAME;
|
||||||
let non_overflow_block = context.append_basic_block("shift_left_non_overflow");
|
let declaration = <PolkaVMShlFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
let join_block = context.append_basic_block("shift_left_join");
|
Ok(context
|
||||||
|
.build_call(declaration, &[shift.into(), value.into()], "SHL")
|
||||||
let condition_is_overflow = context.builder().build_int_compare(
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
inkwell::IntPredicate::UGT,
|
|
||||||
shift,
|
|
||||||
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
|
|
||||||
"shift_left_is_overflow",
|
|
||||||
)?;
|
|
||||||
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
|
|
||||||
|
|
||||||
context.set_basic_block(overflow_block);
|
|
||||||
context.build_unconditional_branch(join_block);
|
|
||||||
|
|
||||||
context.set_basic_block(non_overflow_block);
|
|
||||||
let value =
|
|
||||||
context
|
|
||||||
.builder()
|
|
||||||
.build_left_shift(value, shift, "shift_left_non_overflow_result")?;
|
|
||||||
context.build_unconditional_branch(join_block);
|
|
||||||
|
|
||||||
context.set_basic_block(join_block);
|
|
||||||
let result = context
|
|
||||||
.builder()
|
|
||||||
.build_phi(context.word_type(), "shift_left_value")?;
|
|
||||||
result.add_incoming(&[
|
|
||||||
(&value, non_overflow_block),
|
|
||||||
(&context.word_const(0), overflow_block),
|
|
||||||
]);
|
|
||||||
Ok(result.as_basic_value())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the bitwise shift right.
|
/// Translates the bitwise shift right.
|
||||||
@@ -101,39 +81,11 @@ pub fn shift_right<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
let overflow_block = context.append_basic_block("shift_right_overflow");
|
let name = <PolkaVMShrFunction as RuntimeFunction<D>>::NAME;
|
||||||
let non_overflow_block = context.append_basic_block("shift_right_non_overflow");
|
let declaration = <PolkaVMShrFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
let join_block = context.append_basic_block("shift_right_join");
|
Ok(context
|
||||||
|
.build_call(declaration, &[shift.into(), value.into()], "SHR")
|
||||||
let condition_is_overflow = context.builder().build_int_compare(
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
inkwell::IntPredicate::UGT,
|
|
||||||
shift,
|
|
||||||
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
|
|
||||||
"shift_right_is_overflow",
|
|
||||||
)?;
|
|
||||||
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
|
|
||||||
|
|
||||||
context.set_basic_block(overflow_block);
|
|
||||||
context.build_unconditional_branch(join_block);
|
|
||||||
|
|
||||||
context.set_basic_block(non_overflow_block);
|
|
||||||
let value = context.builder().build_right_shift(
|
|
||||||
value,
|
|
||||||
shift,
|
|
||||||
false,
|
|
||||||
"shift_right_non_overflow_result",
|
|
||||||
)?;
|
|
||||||
context.build_unconditional_branch(join_block);
|
|
||||||
|
|
||||||
context.set_basic_block(join_block);
|
|
||||||
let result = context
|
|
||||||
.builder()
|
|
||||||
.build_phi(context.word_type(), "shift_right_value")?;
|
|
||||||
result.add_incoming(&[
|
|
||||||
(&value, non_overflow_block),
|
|
||||||
(&context.word_const(0), overflow_block),
|
|
||||||
]);
|
|
||||||
Ok(result.as_basic_value())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the arithmetic bitwise shift right.
|
/// Translates the arithmetic bitwise shift right.
|
||||||
@@ -145,68 +97,11 @@ pub fn shift_right_arithmetic<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
let overflow_block = context.append_basic_block("shift_right_arithmetic_overflow");
|
let name = <PolkaVMSarFunction as RuntimeFunction<D>>::NAME;
|
||||||
let overflow_positive_block =
|
let declaration = <PolkaVMSarFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
context.append_basic_block("shift_right_arithmetic_overflow_positive");
|
Ok(context
|
||||||
let overflow_negative_block =
|
.build_call(declaration, &[shift.into(), value.into()], "SHR")
|
||||||
context.append_basic_block("shift_right_arithmetic_overflow_negative");
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
let non_overflow_block = context.append_basic_block("shift_right_arithmetic_non_overflow");
|
|
||||||
let join_block = context.append_basic_block("shift_right_arithmetic_join");
|
|
||||||
|
|
||||||
let condition_is_overflow = context.builder().build_int_compare(
|
|
||||||
inkwell::IntPredicate::UGT,
|
|
||||||
shift,
|
|
||||||
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
|
|
||||||
"shift_right_arithmetic_is_overflow",
|
|
||||||
)?;
|
|
||||||
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
|
|
||||||
|
|
||||||
context.set_basic_block(overflow_block);
|
|
||||||
let sign_bit = context.builder().build_right_shift(
|
|
||||||
value,
|
|
||||||
context.word_const((revive_common::BIT_LENGTH_WORD - 1) as u64),
|
|
||||||
false,
|
|
||||||
"shift_right_arithmetic_sign_bit",
|
|
||||||
)?;
|
|
||||||
let condition_is_negative = context.builder().build_int_truncate_or_bit_cast(
|
|
||||||
sign_bit,
|
|
||||||
context.bool_type(),
|
|
||||||
"shift_right_arithmetic_sign_bit_truncated",
|
|
||||||
)?;
|
|
||||||
context.build_conditional_branch(
|
|
||||||
condition_is_negative,
|
|
||||||
overflow_negative_block,
|
|
||||||
overflow_positive_block,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
context.set_basic_block(overflow_positive_block);
|
|
||||||
context.build_unconditional_branch(join_block);
|
|
||||||
|
|
||||||
context.set_basic_block(overflow_negative_block);
|
|
||||||
context.build_unconditional_branch(join_block);
|
|
||||||
|
|
||||||
context.set_basic_block(non_overflow_block);
|
|
||||||
let value = context.builder().build_right_shift(
|
|
||||||
value,
|
|
||||||
shift,
|
|
||||||
true,
|
|
||||||
"shift_right_arithmetic_non_overflow_result",
|
|
||||||
)?;
|
|
||||||
context.build_unconditional_branch(join_block);
|
|
||||||
|
|
||||||
context.set_basic_block(join_block);
|
|
||||||
let result = context
|
|
||||||
.builder()
|
|
||||||
.build_phi(context.word_type(), "shift_arithmetic_right_value")?;
|
|
||||||
result.add_incoming(&[
|
|
||||||
(&value, non_overflow_block),
|
|
||||||
(
|
|
||||||
&context.word_type().const_all_ones(),
|
|
||||||
overflow_negative_block,
|
|
||||||
),
|
|
||||||
(&context.word_const(0), overflow_positive_block),
|
|
||||||
]);
|
|
||||||
Ok(result.as_basic_value())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the `byte` instruction, extracting the byte of `operand_2`
|
/// Translates the `byte` instruction, extracting the byte of `operand_2`
|
||||||
@@ -225,61 +120,9 @@ pub fn byte<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
const MAX_INDEX_BYTES: u64 = 31;
|
let name = <PolkaVMByteFunction as RuntimeFunction<D>>::NAME;
|
||||||
|
let declaration = <PolkaVMByteFunction as RuntimeFunction<D>>::declaration(context);
|
||||||
let is_overflow_bit = context.builder().build_int_compare(
|
Ok(context
|
||||||
inkwell::IntPredicate::ULE,
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "BYTE")
|
||||||
operand_1,
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value")))
|
||||||
context.word_const(MAX_INDEX_BYTES),
|
|
||||||
"is_overflow_bit",
|
|
||||||
)?;
|
|
||||||
let is_overflow_byte = context.builder().build_int_z_extend(
|
|
||||||
is_overflow_bit,
|
|
||||||
context.byte_type(),
|
|
||||||
"is_overflow_byte",
|
|
||||||
)?;
|
|
||||||
let mask_byte = context.builder().build_int_mul(
|
|
||||||
context.byte_type().const_all_ones(),
|
|
||||||
is_overflow_byte,
|
|
||||||
"mask_byte",
|
|
||||||
)?;
|
|
||||||
let mask_byte_word =
|
|
||||||
context
|
|
||||||
.builder()
|
|
||||||
.build_int_z_extend(mask_byte, context.word_type(), "mask_byte_word")?;
|
|
||||||
|
|
||||||
let index_truncated =
|
|
||||||
context
|
|
||||||
.builder()
|
|
||||||
.build_int_truncate(operand_1, context.byte_type(), "index_truncated")?;
|
|
||||||
let index_in_bits = context.builder().build_int_mul(
|
|
||||||
index_truncated,
|
|
||||||
context
|
|
||||||
.byte_type()
|
|
||||||
.const_int(revive_common::BIT_LENGTH_BYTE as u64, false),
|
|
||||||
"index_in_bits",
|
|
||||||
)?;
|
|
||||||
let index_from_most_significant_bit = context.builder().build_int_sub(
|
|
||||||
context.byte_type().const_int(
|
|
||||||
MAX_INDEX_BYTES * revive_common::BIT_LENGTH_BYTE as u64,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
index_in_bits,
|
|
||||||
"index_from_msb",
|
|
||||||
)?;
|
|
||||||
let index_extended = context.builder().build_int_z_extend(
|
|
||||||
index_from_most_significant_bit,
|
|
||||||
context.word_type(),
|
|
||||||
"index",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mask = context
|
|
||||||
.builder()
|
|
||||||
.build_left_shift(mask_byte_word, index_extended, "mask")?;
|
|
||||||
let masked_value = context.builder().build_and(operand_2, mask, "masked")?;
|
|
||||||
let byte = context
|
|
||||||
.builder()
|
|
||||||
.build_right_shift(masked_value, index_extended, false, "byte")?;
|
|
||||||
|
|
||||||
Ok(byte.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@
|
|||||||
use inkwell::values::BasicValue;
|
use inkwell::values::BasicValue;
|
||||||
|
|
||||||
use crate::polkavm::context::argument::Argument;
|
use crate::polkavm::context::argument::Argument;
|
||||||
|
use crate::polkavm::context::runtime::RuntimeFunction;
|
||||||
use crate::polkavm::context::Context;
|
use crate::polkavm::context::Context;
|
||||||
use crate::polkavm::Dependency;
|
use crate::polkavm::Dependency;
|
||||||
|
use crate::{PolkaVMCall, PolkaVMCallReentrancyProtector};
|
||||||
|
|
||||||
const STATIC_CALL_FLAG: u32 = 0b0001_0000;
|
const STATIC_CALL_FLAG: u32 = 0b0001_0000;
|
||||||
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
|
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
|
||||||
const SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD: u64 = 2300;
|
|
||||||
|
|
||||||
/// Translates a contract call.
|
/// Translates a contract call.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
@@ -27,79 +28,45 @@ pub fn call<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
let address_pointer = context.build_address_argument_store(address)?;
|
|
||||||
|
|
||||||
let value = value.unwrap_or_else(|| context.word_const(0));
|
|
||||||
let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer");
|
|
||||||
context.build_store(value_pointer, value)?;
|
|
||||||
|
|
||||||
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
|
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
|
||||||
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
|
|
||||||
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
|
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
|
||||||
|
|
||||||
|
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
|
||||||
let output_length = context.safe_truncate_int_to_xlen(output_length)?;
|
let output_length = context.safe_truncate_int_to_xlen(output_length)?;
|
||||||
|
|
||||||
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
|
let deposit_limit_pointer =
|
||||||
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
|
context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
|
||||||
|
let flags = if static_call {
|
||||||
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
|
||||||
context.build_store(output_length_pointer, output_length)?;
|
|
||||||
|
|
||||||
let (flags, deposit_limit_value) = if static_call {
|
|
||||||
let flags = REENTRANT_CALL_FLAG | STATIC_CALL_FLAG;
|
let flags = REENTRANT_CALL_FLAG | STATIC_CALL_FLAG;
|
||||||
(
|
context.build_store(deposit_limit_pointer, context.word_type().const_zero())?;
|
||||||
context.xlen_type().const_int(flags as u64, false),
|
context.xlen_type().const_int(flags as u64, false)
|
||||||
context.word_type().const_zero(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
call_reentrancy_heuristic(context, gas, input_length, output_length)?
|
call_reentrancy_heuristic(
|
||||||
|
context,
|
||||||
|
deposit_limit_pointer.value,
|
||||||
|
gas,
|
||||||
|
input_length,
|
||||||
|
output_length,
|
||||||
|
)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
|
let value = value.unwrap_or_else(|| context.word_const(0));
|
||||||
context.build_store(deposit_pointer, deposit_limit_value)?;
|
|
||||||
|
|
||||||
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
let name = <PolkaVMCall as RuntimeFunction<D>>::NAME;
|
||||||
context.builder(),
|
let declaration = <PolkaVMCall as RuntimeFunction<D>>::declaration(context);
|
||||||
context.llvm(),
|
let arguments = &[
|
||||||
flags,
|
address.into(),
|
||||||
address_pointer.to_int(context),
|
value.into(),
|
||||||
"address_and_callee",
|
input_offset.into(),
|
||||||
)?;
|
input_length.into(),
|
||||||
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
output_offset.into(),
|
||||||
context.builder(),
|
output_length.into(),
|
||||||
context.llvm(),
|
deposit_limit_pointer.value.into(),
|
||||||
deposit_pointer.to_int(context),
|
flags.into(),
|
||||||
value_pointer.to_int(context),
|
];
|
||||||
"deposit_and_value",
|
|
||||||
)?;
|
|
||||||
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
|
||||||
context.builder(),
|
|
||||||
context.llvm(),
|
|
||||||
input_length,
|
|
||||||
input_pointer.to_int(context),
|
|
||||||
"input_data",
|
|
||||||
)?;
|
|
||||||
let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
|
||||||
context.builder(),
|
|
||||||
context.llvm(),
|
|
||||||
output_length_pointer.to_int(context),
|
|
||||||
output_pointer.to_int(context),
|
|
||||||
"output_data",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let name = revive_runtime_api::polkavm_imports::CALL;
|
|
||||||
let success = context
|
let success = context
|
||||||
.build_runtime_call(
|
.build_call(declaration, arguments, "call")
|
||||||
name,
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))
|
||||||
&[
|
|
||||||
flags_and_callee.into(),
|
|
||||||
context.register_type().const_all_ones().into(),
|
|
||||||
context.register_type().const_all_ones().into(),
|
|
||||||
deposit_and_value.into(),
|
|
||||||
input_data.into(),
|
|
||||||
output_data.into(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|| panic!("{name} should return a value"))
|
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
|
|
||||||
let is_success = context.builder().build_int_compare(
|
let is_success = context.builder().build_int_compare(
|
||||||
@@ -216,85 +183,28 @@ where
|
|||||||
.as_basic_value_enum())
|
.as_basic_value_enum())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The Solidity `address.transfer` and `address.send` call detection heuristic.
|
|
||||||
///
|
|
||||||
/// # Why
|
|
||||||
/// This heuristic is an additional security feature to guard against re-entrancy attacks
|
|
||||||
/// in case contract authors violate Solidity best practices and use `address.transfer` or
|
|
||||||
/// `address.send`.
|
|
||||||
/// While contract authors are supposed to never use `address.transfer` or `address.send`,
|
|
||||||
/// for a small cost we can be extra defensive about it.
|
|
||||||
///
|
|
||||||
/// # How
|
|
||||||
/// The gas stipend emitted by solc for `transfer` and `send` is not static, thus:
|
|
||||||
/// - Dynamically allow re-entrancy only for calls considered not transfer or send.
|
|
||||||
/// - Detected balance transfers will supply 0 deposit limit instead of `u256::MAX`.
|
|
||||||
///
|
|
||||||
/// Calls are considered transfer or send if:
|
|
||||||
/// - (Input length | Output lenght) == 0;
|
|
||||||
/// - Gas <= 2300;
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// The call flags xlen `IntValue` and the deposit limit word `IntValue`.
|
|
||||||
fn call_reentrancy_heuristic<'ctx, D>(
|
fn call_reentrancy_heuristic<'ctx, D>(
|
||||||
context: &mut Context<'ctx, D>,
|
context: &mut Context<'ctx, D>,
|
||||||
|
deposit_limit_pointer: inkwell::values::PointerValue<'ctx>,
|
||||||
gas: inkwell::values::IntValue<'ctx>,
|
gas: inkwell::values::IntValue<'ctx>,
|
||||||
input_length: inkwell::values::IntValue<'ctx>,
|
input_length: inkwell::values::IntValue<'ctx>,
|
||||||
output_length: inkwell::values::IntValue<'ctx>,
|
output_length: inkwell::values::IntValue<'ctx>,
|
||||||
) -> anyhow::Result<(
|
) -> anyhow::Result<inkwell::values::IntValue<'ctx>>
|
||||||
inkwell::values::IntValue<'ctx>,
|
|
||||||
inkwell::values::IntValue<'ctx>,
|
|
||||||
)>
|
|
||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
// Branch-free SSA implementation: First derive the heuristic boolean (int1) value.
|
let name = <PolkaVMCallReentrancyProtector as RuntimeFunction<D>>::NAME;
|
||||||
let input_length_or_output_length =
|
let declaration = <PolkaVMCallReentrancyProtector as RuntimeFunction<D>>::declaration(context);
|
||||||
|
let arguments = &[
|
||||||
|
deposit_limit_pointer.into(),
|
||||||
|
gas.into(),
|
||||||
context
|
context
|
||||||
.builder()
|
.builder()
|
||||||
.build_or(input_length, output_length, "input_length_or_output_length")?;
|
.build_or(input_length, output_length, "input_length_or_output_length")?
|
||||||
let is_no_input_no_output = context.builder().build_int_compare(
|
.into(),
|
||||||
inkwell::IntPredicate::EQ,
|
];
|
||||||
context.xlen_type().const_zero(),
|
Ok(context
|
||||||
input_length_or_output_length,
|
.build_call(declaration, arguments, "call_flags")
|
||||||
"is_no_input_no_output",
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value"))
|
||||||
)?;
|
.into_int_value())
|
||||||
let gas_stipend = context
|
|
||||||
.word_type()
|
|
||||||
.const_int(SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD, false);
|
|
||||||
let is_gas_stipend_for_transfer_or_send = context.builder().build_int_compare(
|
|
||||||
inkwell::IntPredicate::ULE,
|
|
||||||
gas,
|
|
||||||
gas_stipend,
|
|
||||||
"is_gas_stipend_for_transfer_or_send",
|
|
||||||
)?;
|
|
||||||
let is_balance_transfer = context.builder().build_and(
|
|
||||||
is_no_input_no_output,
|
|
||||||
is_gas_stipend_for_transfer_or_send,
|
|
||||||
"is_balance_transfer",
|
|
||||||
)?;
|
|
||||||
let is_regular_call = context
|
|
||||||
.builder()
|
|
||||||
.build_not(is_balance_transfer, "is_balance_transfer_inverted")?;
|
|
||||||
|
|
||||||
// Call flag: Left shift the heuristic boolean value.
|
|
||||||
let is_regular_call_xlen = context.builder().build_int_z_extend(
|
|
||||||
is_regular_call,
|
|
||||||
context.xlen_type(),
|
|
||||||
"is_balance_transfer_xlen",
|
|
||||||
)?;
|
|
||||||
let call_flags = context.builder().build_left_shift(
|
|
||||||
is_regular_call_xlen,
|
|
||||||
context.xlen_type().const_int(3, false),
|
|
||||||
"flags",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Deposit limit value: Sign-extended the heuristic boolean value.
|
|
||||||
let deposit_limit_value = context.builder().build_int_s_extend(
|
|
||||||
is_regular_call,
|
|
||||||
context.word_type(),
|
|
||||||
"deposit_limit_value",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok((call_flags, deposit_limit_value))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ pub fn yul<T: Compiler>(
|
|||||||
solc: &mut T,
|
solc: &mut T,
|
||||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||||
include_metadata_hash: bool,
|
include_metadata_hash: bool,
|
||||||
mut debug_config: revive_llvm_context::DebugConfig,
|
debug_config: revive_llvm_context::DebugConfig,
|
||||||
llvm_arguments: &[String],
|
llvm_arguments: &[String],
|
||||||
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
|
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
|
||||||
) -> anyhow::Result<Build> {
|
) -> anyhow::Result<Build> {
|
||||||
@@ -77,7 +77,6 @@ pub fn yul<T: Compiler>(
|
|||||||
let solc_validator = Some(&*solc);
|
let solc_validator = Some(&*solc);
|
||||||
let project = Project::try_from_yul_path(path, solc_validator)?;
|
let project = Project::try_from_yul_path(path, solc_validator)?;
|
||||||
|
|
||||||
debug_config.set_yul_path(path);
|
|
||||||
let build = project.compile(
|
let build = project.compile(
|
||||||
optimizer_settings,
|
optimizer_settings,
|
||||||
include_metadata_hash,
|
include_metadata_hash,
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ impl Contract {
|
|||||||
project: Project,
|
project: Project,
|
||||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||||
include_metadata_hash: bool,
|
include_metadata_hash: bool,
|
||||||
mut debug_config: revive_llvm_context::DebugConfig,
|
debug_config: revive_llvm_context::DebugConfig,
|
||||||
llvm_arguments: &[String],
|
llvm_arguments: &[String],
|
||||||
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
|
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
|
||||||
) -> anyhow::Result<ContractBuild> {
|
) -> anyhow::Result<ContractBuild> {
|
||||||
@@ -117,7 +117,6 @@ impl Contract {
|
|||||||
_ => llvm.create_module(self.path.as_str()),
|
_ => llvm.create_module(self.path.as_str()),
|
||||||
};
|
};
|
||||||
|
|
||||||
debug_config.set_contract_path(&self.path);
|
|
||||||
let mut context = revive_llvm_context::PolkaVMContext::new(
|
let mut context = revive_llvm_context::PolkaVMContext::new(
|
||||||
&llvm,
|
&llvm,
|
||||||
module,
|
module,
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ impl Compiler for SolcCompiler {
|
|||||||
include_paths: Vec<String>,
|
include_paths: Vec<String>,
|
||||||
allow_paths: Option<String>,
|
allow_paths: Option<String>,
|
||||||
) -> anyhow::Result<SolcStandardJsonOutput> {
|
) -> anyhow::Result<SolcStandardJsonOutput> {
|
||||||
|
let version = self.version()?.validate(&include_paths)?.default;
|
||||||
|
|
||||||
let mut command = std::process::Command::new(self.executable.as_str());
|
let mut command = std::process::Command::new(self.executable.as_str());
|
||||||
command.stdin(std::process::Stdio::piped());
|
command.stdin(std::process::Stdio::piped());
|
||||||
command.stdout(std::process::Stdio::piped());
|
command.stdout(std::process::Stdio::piped());
|
||||||
@@ -63,7 +65,7 @@ impl Compiler for SolcCompiler {
|
|||||||
command.arg(allow_paths);
|
command.arg(allow_paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
input.normalize();
|
input.normalize(&version);
|
||||||
|
|
||||||
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
|
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ impl Compiler for SoljsonCompiler {
|
|||||||
anyhow::bail!("configuring allow paths is not supported with solJson")
|
anyhow::bail!("configuring allow paths is not supported with solJson")
|
||||||
}
|
}
|
||||||
|
|
||||||
input.normalize();
|
let version = self.version()?.validate(&include_paths)?.default;
|
||||||
|
input.normalize(&version);
|
||||||
|
|
||||||
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
|
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ pub use self::combined_json::contract::Contract as CombinedJsonContract;
|
|||||||
pub use self::standard_json::input::language::Language as SolcStandardJsonInputLanguage;
|
pub use self::standard_json::input::language::Language as SolcStandardJsonInputLanguage;
|
||||||
pub use self::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
|
pub use self::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
|
||||||
pub use self::standard_json::input::settings::metadata_hash::MetadataHash as SolcStandardJsonInputSettingsMetadataHash;
|
pub use self::standard_json::input::settings::metadata_hash::MetadataHash as SolcStandardJsonInputSettingsMetadataHash;
|
||||||
pub use self::standard_json::input::settings::optimizer::yul_details::YulDetails as SolcStandardJsonInputSettingsYulOptimizerDetails;
|
|
||||||
pub use self::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
|
pub use self::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
|
||||||
pub use self::standard_json::input::settings::polkavm::memory::MemoryConfig as SolcStandardJsonInputSettingsPolkaVMMemory;
|
pub use self::standard_json::input::settings::polkavm::memory::MemoryConfig as SolcStandardJsonInputSettingsPolkaVMMemory;
|
||||||
pub use self::standard_json::input::settings::polkavm::memory::DEFAULT_HEAP_SIZE as PolkaVMDefaultHeapMemorySize;
|
pub use self::standard_json::input::settings::polkavm::memory::DEFAULT_HEAP_SIZE as PolkaVMDefaultHeapMemorySize;
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ impl Input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the necessary defaults.
|
/// Sets the necessary defaults.
|
||||||
pub fn normalize(&mut self) {
|
pub fn normalize(&mut self, version: &semver::Version) {
|
||||||
self.settings.normalize();
|
self.settings.normalize(version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,9 +74,9 @@ impl Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the necessary defaults.
|
/// Sets the necessary defaults.
|
||||||
pub fn normalize(&mut self) {
|
pub fn normalize(&mut self, version: &semver::Version) {
|
||||||
self.polkavm = None;
|
self.polkavm = None;
|
||||||
self.optimizer.normalize();
|
self.optimizer.normalize(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the library list and returns their double hashmap with path and name as keys.
|
/// Parses the library list and returns their double hashmap with path and name as keys.
|
||||||
|
|||||||
@@ -3,54 +3,37 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::standard_json::input::settings::optimizer::yul_details::YulDetails;
|
|
||||||
|
|
||||||
/// The `solc --standard-json` input settings optimizer details.
|
/// The `solc --standard-json` input settings optimizer details.
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Details {
|
pub struct Details {
|
||||||
/// Whether the pass is enabled.
|
/// Whether the pass is enabled.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
pub peephole: bool,
|
||||||
pub peephole: Option<bool>,
|
|
||||||
/// Whether the pass is enabled.
|
/// Whether the pass is enabled.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub inliner: Option<bool>,
|
pub inliner: Option<bool>,
|
||||||
/// Whether the pass is enabled.
|
/// Whether the pass is enabled.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
pub jumpdest_remover: bool,
|
||||||
pub jumpdest_remover: Option<bool>,
|
|
||||||
/// Whether the pass is enabled.
|
/// Whether the pass is enabled.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
pub order_literals: bool,
|
||||||
pub order_literals: Option<bool>,
|
|
||||||
/// Whether the pass is enabled.
|
/// Whether the pass is enabled.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
pub deduplicate: bool,
|
||||||
pub deduplicate: Option<bool>,
|
|
||||||
/// Whether the pass is enabled.
|
/// Whether the pass is enabled.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
pub cse: bool,
|
||||||
pub cse: Option<bool>,
|
|
||||||
/// Whether the pass is enabled.
|
/// Whether the pass is enabled.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
pub constant_optimizer: bool,
|
||||||
pub constant_optimizer: Option<bool>,
|
|
||||||
/// Whether the YUL optimizer is enabled.
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub yul: Option<bool>,
|
|
||||||
/// The YUL optimizer configuration.
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub yul_details: Option<YulDetails>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Details {
|
impl Details {
|
||||||
/// A shortcut constructor.
|
/// A shortcut constructor.
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
peephole: Option<bool>,
|
peephole: bool,
|
||||||
inliner: Option<bool>,
|
inliner: Option<bool>,
|
||||||
jumpdest_remover: Option<bool>,
|
jumpdest_remover: bool,
|
||||||
order_literals: Option<bool>,
|
order_literals: bool,
|
||||||
deduplicate: Option<bool>,
|
deduplicate: bool,
|
||||||
cse: Option<bool>,
|
cse: bool,
|
||||||
constant_optimizer: Option<bool>,
|
constant_optimizer: bool,
|
||||||
yul: Option<bool>,
|
|
||||||
yul_details: Option<YulDetails>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
peephole,
|
peephole,
|
||||||
@@ -60,11 +43,10 @@ impl Details {
|
|||||||
deduplicate,
|
deduplicate,
|
||||||
cse,
|
cse,
|
||||||
constant_optimizer,
|
constant_optimizer,
|
||||||
yul,
|
|
||||||
yul_details,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a set of disabled optimizations.
|
||||||
pub fn disabled(version: &semver::Version) -> Self {
|
pub fn disabled(version: &semver::Version) -> Self {
|
||||||
let inliner = if version >= &semver::Version::new(0, 8, 5) {
|
let inliner = if version >= &semver::Version::new(0, 8, 5) {
|
||||||
Some(false)
|
Some(false)
|
||||||
@@ -72,16 +54,6 @@ impl Details {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::new(
|
Self::new(false, inliner, false, false, false, false, false)
|
||||||
Some(false),
|
|
||||||
inliner,
|
|
||||||
Some(false),
|
|
||||||
Some(false),
|
|
||||||
Some(false),
|
|
||||||
Some(false),
|
|
||||||
Some(false),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
//! The `solc --standard-json` input settings optimizer.
|
//! The `solc --standard-json` input settings optimizer.
|
||||||
|
|
||||||
pub mod details;
|
pub mod details;
|
||||||
pub mod yul_details;
|
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@@ -42,8 +41,13 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the necessary defaults.
|
/// Sets the necessary defaults.
|
||||||
pub fn normalize(&mut self) {
|
pub fn normalize(&mut self, version: &semver::Version) {
|
||||||
self.mode = None;
|
self.mode = None;
|
||||||
self.fallback_to_optimizing_for_size = None;
|
self.fallback_to_optimizing_for_size = None;
|
||||||
|
self.details = if version >= &semver::Version::new(0, 5, 5) {
|
||||||
|
Some(Details::disabled(version))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
//! The `solc --standard-json` input settings YUL optimizer details.
|
|
||||||
|
|
||||||
use serde::Deserialize;
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
/// The `solc --standard-json` input settings optimizer YUL details.
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct YulDetails {
|
|
||||||
/// Whether the stack allocation pass is enabled.
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub stack_allocation: Option<bool>,
|
|
||||||
/// The optimization step sequence string.
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub optimizer_steps: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl YulDetails {
|
|
||||||
/// A shortcut constructor.
|
|
||||||
pub fn new(stack_allocation: Option<bool>, optimizer_steps: Option<String>) -> Self {
|
|
||||||
Self {
|
|
||||||
stack_allocation,
|
|
||||||
optimizer_steps,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -130,8 +130,15 @@ where
|
|||||||
let identifier = self.bindings.remove(0);
|
let identifier = self.bindings.remove(0);
|
||||||
let pointer = context
|
let pointer = context
|
||||||
.current_function()
|
.current_function()
|
||||||
.borrow_mut()
|
.borrow()
|
||||||
.get_stack_pointer(context, identifier.inner.clone());
|
.get_stack_pointer(identifier.inner.as_str())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"{} Assignment to an undeclared variable `{}`",
|
||||||
|
identifier.location,
|
||||||
|
identifier.inner,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
context.build_store(pointer, value.access(context)?)?;
|
context.build_store(pointer, value.access(context)?)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -158,8 +165,15 @@ where
|
|||||||
|
|
||||||
let binding_pointer = context
|
let binding_pointer = context
|
||||||
.current_function()
|
.current_function()
|
||||||
.borrow_mut()
|
.borrow()
|
||||||
.get_stack_pointer(context, binding.inner.clone());
|
.get_stack_pointer(binding.inner.as_str())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"{} Assignment to an undeclared variable `{}`",
|
||||||
|
binding.location,
|
||||||
|
binding.inner,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
let value = context.build_load(
|
let value = context.build_load(
|
||||||
field_pointer,
|
field_pointer,
|
||||||
format!("assignment_binding_{index}_value").as_str(),
|
format!("assignment_binding_{index}_value").as_str(),
|
||||||
|
|||||||
@@ -123,7 +123,6 @@ impl FunctionCall {
|
|||||||
D: revive_llvm_context::PolkaVMDependency + Clone,
|
D: revive_llvm_context::PolkaVMDependency + Clone,
|
||||||
{
|
{
|
||||||
let location = self.location;
|
let location = self.location;
|
||||||
context.set_debug_location(location.line, 0, None)?;
|
|
||||||
|
|
||||||
match self.name {
|
match self.name {
|
||||||
Name::UserDefined(name) => {
|
Name::UserDefined(name) => {
|
||||||
|
|||||||
@@ -123,8 +123,11 @@ impl Expression {
|
|||||||
|
|
||||||
let pointer = context
|
let pointer = context
|
||||||
.current_function()
|
.current_function()
|
||||||
.borrow_mut()
|
.borrow()
|
||||||
.get_stack_pointer(context, id.clone());
|
.get_stack_pointer(&id)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow::anyhow!("{} Undeclared variable `{}`", identifier.location, id)
|
||||||
|
})?;
|
||||||
|
|
||||||
let constant = context.current_function().borrow().yul().get_constant(&id);
|
let constant = context.current_function().borrow().yul().get_constant(&id);
|
||||||
|
|
||||||
|
|||||||
@@ -212,7 +212,6 @@ where
|
|||||||
function_type,
|
function_type,
|
||||||
self.result.len(),
|
self.result.len(),
|
||||||
Some(inkwell::module::Linkage::External),
|
Some(inkwell::module::Linkage::External),
|
||||||
1024,
|
|
||||||
)?;
|
)?;
|
||||||
revive_llvm_context::PolkaVMFunction::set_attributes(
|
revive_llvm_context::PolkaVMFunction::set_attributes(
|
||||||
context.llvm(),
|
context.llvm(),
|
||||||
@@ -245,7 +244,7 @@ where
|
|||||||
context
|
context
|
||||||
.current_function()
|
.current_function()
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert_stack_pointer(context, identifier.inner);
|
.insert_stack_pointer(identifier.inner, pointer);
|
||||||
}
|
}
|
||||||
revive_llvm_context::PolkaVMFunctionReturn::Compound { pointer, .. } => {
|
revive_llvm_context::PolkaVMFunctionReturn::Compound { pointer, .. } => {
|
||||||
for (index, identifier) in self.result.into_iter().enumerate() {
|
for (index, identifier) in self.result.into_iter().enumerate() {
|
||||||
@@ -265,24 +264,25 @@ where
|
|||||||
context
|
context
|
||||||
.current_function()
|
.current_function()
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert_stack_pointer(context, identifier.inner.clone());
|
.insert_stack_pointer(identifier.inner.clone(), pointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//let argument_types: Vec<_> = self
|
let argument_types: Vec<_> = self
|
||||||
// .arguments
|
.arguments
|
||||||
// .iter()
|
.iter()
|
||||||
// .map(|argument| {
|
.map(|argument| {
|
||||||
// let yul_type = argument.r#type.to_owned().unwrap_or_default();
|
let yul_type = argument.r#type.to_owned().unwrap_or_default();
|
||||||
// yul_type.into_llvm(context)
|
yul_type.into_llvm(context)
|
||||||
// })
|
})
|
||||||
// .collect();
|
.collect();
|
||||||
for (index, argument) in self.arguments.iter().enumerate() {
|
for (index, argument) in self.arguments.iter().enumerate() {
|
||||||
let pointer = context
|
let pointer = context.build_alloca(argument_types[index], argument.inner.as_str());
|
||||||
|
context
|
||||||
.current_function()
|
.current_function()
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert_stack_pointer(context, argument.inner.clone());
|
.insert_stack_pointer(argument.inner.clone(), pointer);
|
||||||
context.build_store(
|
context.build_store(
|
||||||
pointer,
|
pointer,
|
||||||
context.current_function().borrow().get_nth_param(index),
|
context.current_function().borrow().get_nth_param(index),
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ where
|
|||||||
D: revive_llvm_context::PolkaVMDependency + Clone,
|
D: revive_llvm_context::PolkaVMDependency + Clone,
|
||||||
{
|
{
|
||||||
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
|
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
|
||||||
context.set_debug_location(self.location.line, 0, None)?;
|
|
||||||
let condition = self
|
let condition = self
|
||||||
.condition
|
.condition
|
||||||
.into_llvm(context)?
|
.into_llvm(context)?
|
||||||
|
|||||||
@@ -204,11 +204,25 @@ where
|
|||||||
revive_llvm_context::PolkaVMEventLogFunction::<3>.declare(context)?;
|
revive_llvm_context::PolkaVMEventLogFunction::<3>.declare(context)?;
|
||||||
revive_llvm_context::PolkaVMEventLogFunction::<4>.declare(context)?;
|
revive_llvm_context::PolkaVMEventLogFunction::<4>.declare(context)?;
|
||||||
|
|
||||||
|
revive_llvm_context::PolkaVMAdditionFunction.declare(context)?;
|
||||||
|
revive_llvm_context::PolkaVMSubstractionFunction.declare(context)?;
|
||||||
|
revive_llvm_context::PolkaVMMultiplicationFunction.declare(context)?;
|
||||||
revive_llvm_context::PolkaVMDivisionFunction.declare(context)?;
|
revive_llvm_context::PolkaVMDivisionFunction.declare(context)?;
|
||||||
revive_llvm_context::PolkaVMSignedDivisionFunction.declare(context)?;
|
revive_llvm_context::PolkaVMSignedDivisionFunction.declare(context)?;
|
||||||
revive_llvm_context::PolkaVMRemainderFunction.declare(context)?;
|
revive_llvm_context::PolkaVMRemainderFunction.declare(context)?;
|
||||||
revive_llvm_context::PolkaVMSignedRemainderFunction.declare(context)?;
|
revive_llvm_context::PolkaVMSignedRemainderFunction.declare(context)?;
|
||||||
|
|
||||||
|
revive_llvm_context::PolkaVMOrFunction.declare(context)?;
|
||||||
|
revive_llvm_context::PolkaVMXorFunction.declare(context)?;
|
||||||
|
revive_llvm_context::PolkaVMAndFunction.declare(context)?;
|
||||||
|
revive_llvm_context::PolkaVMShlFunction.declare(context)?;
|
||||||
|
revive_llvm_context::PolkaVMShrFunction.declare(context)?;
|
||||||
|
revive_llvm_context::PolkaVMSarFunction.declare(context)?;
|
||||||
|
revive_llvm_context::PolkaVMByteFunction.declare(context)?;
|
||||||
|
|
||||||
|
revive_llvm_context::PolkaVMCall.declare(context)?;
|
||||||
|
revive_llvm_context::PolkaVMCallReentrancyProtector.declare(context)?;
|
||||||
|
|
||||||
revive_llvm_context::PolkaVMSbrkFunction.declare(context)?;
|
revive_llvm_context::PolkaVMSbrkFunction.declare(context)?;
|
||||||
|
|
||||||
let mut entry = revive_llvm_context::PolkaVMEntryFunction::default();
|
let mut entry = revive_llvm_context::PolkaVMEntryFunction::default();
|
||||||
@@ -258,11 +272,25 @@ where
|
|||||||
revive_llvm_context::PolkaVMEventLogFunction::<3>.into_llvm(context)?;
|
revive_llvm_context::PolkaVMEventLogFunction::<3>.into_llvm(context)?;
|
||||||
revive_llvm_context::PolkaVMEventLogFunction::<4>.into_llvm(context)?;
|
revive_llvm_context::PolkaVMEventLogFunction::<4>.into_llvm(context)?;
|
||||||
|
|
||||||
|
revive_llvm_context::PolkaVMAdditionFunction.into_llvm(context)?;
|
||||||
|
revive_llvm_context::PolkaVMSubstractionFunction.into_llvm(context)?;
|
||||||
|
revive_llvm_context::PolkaVMMultiplicationFunction.into_llvm(context)?;
|
||||||
revive_llvm_context::PolkaVMDivisionFunction.into_llvm(context)?;
|
revive_llvm_context::PolkaVMDivisionFunction.into_llvm(context)?;
|
||||||
revive_llvm_context::PolkaVMSignedDivisionFunction.into_llvm(context)?;
|
revive_llvm_context::PolkaVMSignedDivisionFunction.into_llvm(context)?;
|
||||||
revive_llvm_context::PolkaVMRemainderFunction.into_llvm(context)?;
|
revive_llvm_context::PolkaVMRemainderFunction.into_llvm(context)?;
|
||||||
revive_llvm_context::PolkaVMSignedRemainderFunction.into_llvm(context)?;
|
revive_llvm_context::PolkaVMSignedRemainderFunction.into_llvm(context)?;
|
||||||
|
|
||||||
|
revive_llvm_context::PolkaVMOrFunction.into_llvm(context)?;
|
||||||
|
revive_llvm_context::PolkaVMXorFunction.into_llvm(context)?;
|
||||||
|
revive_llvm_context::PolkaVMAndFunction.into_llvm(context)?;
|
||||||
|
revive_llvm_context::PolkaVMShlFunction.into_llvm(context)?;
|
||||||
|
revive_llvm_context::PolkaVMShrFunction.into_llvm(context)?;
|
||||||
|
revive_llvm_context::PolkaVMSarFunction.into_llvm(context)?;
|
||||||
|
revive_llvm_context::PolkaVMByteFunction.into_llvm(context)?;
|
||||||
|
|
||||||
|
revive_llvm_context::PolkaVMCall.into_llvm(context)?;
|
||||||
|
revive_llvm_context::PolkaVMCallReentrancyProtector.into_llvm(context)?;
|
||||||
|
|
||||||
revive_llvm_context::PolkaVMSbrkFunction.into_llvm(context)?;
|
revive_llvm_context::PolkaVMSbrkFunction.into_llvm(context)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -104,10 +104,11 @@ where
|
|||||||
context.set_debug_location(self.location.line, 0, None)?;
|
context.set_debug_location(self.location.line, 0, None)?;
|
||||||
let identifier_type = identifier.r#type.clone().unwrap_or_default();
|
let identifier_type = identifier.r#type.clone().unwrap_or_default();
|
||||||
let r#type = identifier_type.into_llvm(context);
|
let r#type = identifier_type.into_llvm(context);
|
||||||
let pointer = context
|
let pointer = context.build_alloca(r#type, identifier.inner.as_str());
|
||||||
|
context
|
||||||
.current_function()
|
.current_function()
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert_stack_pointer(context, identifier.inner.clone());
|
.insert_stack_pointer(identifier.inner.clone(), pointer);
|
||||||
|
|
||||||
let value = if let Some(expression) = self.expression {
|
let value = if let Some(expression) = self.expression {
|
||||||
match expression.into_llvm(context)? {
|
match expression.into_llvm(context)? {
|
||||||
@@ -139,11 +140,15 @@ where
|
|||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.into_llvm(context);
|
.into_llvm(context);
|
||||||
let pointer = context
|
let pointer = context.build_alloca(
|
||||||
|
yul_type.as_basic_type_enum(),
|
||||||
|
format!("binding_{index}_pointer").as_str(),
|
||||||
|
);
|
||||||
|
context.build_store(pointer, yul_type.const_zero())?;
|
||||||
|
context
|
||||||
.current_function()
|
.current_function()
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert_stack_pointer(context, binding.inner.to_owned());
|
.insert_stack_pointer(binding.inner.to_owned(), pointer);
|
||||||
context.build_store(pointer, yul_type.const_zero())?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let expression = match self.expression.take() {
|
let expression = match self.expression.take() {
|
||||||
@@ -198,7 +203,14 @@ where
|
|||||||
let pointer = context
|
let pointer = context
|
||||||
.current_function()
|
.current_function()
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.get_stack_pointer(context, binding.inner.to_owned());
|
.get_stack_pointer(binding.inner.as_str())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"{} Assignment to an undeclared variable `{}`",
|
||||||
|
binding.location,
|
||||||
|
binding.inner
|
||||||
|
)
|
||||||
|
})?;
|
||||||
context.build_store(pointer, value)?;
|
context.build_store(pointer, value)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,11 +70,10 @@ impl Type {
|
|||||||
D: revive_llvm_context::PolkaVMDependency + Clone,
|
D: revive_llvm_context::PolkaVMDependency + Clone,
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
//Self::Bool => context.integer_type(revive_common::BIT_LENGTH_BOOLEAN),
|
Self::Bool => context.integer_type(revive_common::BIT_LENGTH_BOOLEAN),
|
||||||
//Self::Int(bitlength) => context.integer_type(bitlength),
|
Self::Int(bitlength) => context.integer_type(bitlength),
|
||||||
Self::UInt(bitlength) => context.integer_type(bitlength),
|
Self::UInt(bitlength) => context.integer_type(bitlength),
|
||||||
//Self::Custom(_) => context.word_type(),
|
Self::Custom(_) => context.word_type(),
|
||||||
_ => unreachable!("no other YUL type is supported"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user