Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions .github/workflows/cranelift-release-branch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
name: Test upcoming Cranelift release branch

on:
schedule:
# Run daily so we don't miss the release branch cut.
- cron: "0 3 * * *"
workflow_dispatch: {}

defaults:
run:
shell: bash

permissions: {}

env:
CARGO_BUILD_INCREMENTAL: false
RUSTFLAGS: "-Dwarnings"

jobs:
test_upcoming_cranelift_release:
runs-on: ubuntu-latest
timeout-minutes: 90

steps:
- uses: actions/checkout@v6

- name: Determine latest Wasmtime release branch
id: wasmtime_release_branch
run: |
set -euo pipefail
branches="$(
git ls-remote --heads https://github.com/bytecodealliance/wasmtime.git "refs/heads/release-*" \
| awk '{print $2}' \
| sed 's#refs/heads/##' \
| sort -V
)"
if [[ -z "${branches}" ]]; then
echo "No wasmtime release branches found"
exit 1
fi
latest="$(echo "${branches}" | tail -n 1)"
echo "Latest release branch: ${latest}"
echo "branch=${latest}" >> "$GITHUB_OUTPUT"

- name: Skip if already tested
id: tested_cache
uses: actions/cache@v5
with:
path: .ci/cranelift-release-tested
key: cranelift-release-tested-${{ steps.wasmtime_release_branch.outputs.branch }}

- name: Mark tested (cache payload)
if: steps.tested_cache.outputs.cache-hit != 'true'
run: |
mkdir -p .ci/cranelift-release-tested
echo "${{ steps.wasmtime_release_branch.outputs.branch }}" > .ci/cranelift-release-tested/branch.txt

- name: Patch Cargo.toml to use release branch Cranelift
if: steps.tested_cache.outputs.cache-hit != 'true'
run: |
set -euo pipefail
branch="${{ steps.wasmtime_release_branch.outputs.branch }}"

python3 - <<'PY'
import pathlib, re, os
cargo_toml = pathlib.Path("Cargo.toml")
s = cargo_toml.read_text(encoding="utf-8")

branch = os.environ["WASMTIME_RELEASE_BRANCH"]
patch_lines = "\n".join([
f'cranelift-codegen = {{ git = "https://github.com/bytecodealliance/wasmtime.git", branch = "{branch}" }}',
f'cranelift-frontend = {{ git = "https://github.com/bytecodealliance/wasmtime.git", branch = "{branch}" }}',
f'cranelift-module = {{ git = "https://github.com/bytecodealliance/wasmtime.git", branch = "{branch}" }}',
f'cranelift-native = {{ git = "https://github.com/bytecodealliance/wasmtime.git", branch = "{branch}" }}',
f'cranelift-jit = {{ git = "https://github.com/bytecodealliance/wasmtime.git", branch = "{branch}" }}',
f'cranelift-object = {{ git = "https://github.com/bytecodealliance/wasmtime.git", branch = "{branch}" }}',
])

# Ensure a [patch.crates-io] section exists.
if "[patch.crates-io]" not in s:
s += "\n\n[patch.crates-io]\n"

# Remove any previous CI-inserted patch block.
s = re.sub(
r"(?ms)^\n?# BEGIN CI WASMTIME RELEASE PATCH\n.*?^\# END CI WASMTIME RELEASE PATCH\n",
"\n",
s,
)

# Insert immediately after the [patch.crates-io] header.
s = re.sub(
r"(?m)^\[patch\.crates-io\]\s*$",
"[patch.crates-io]\n"
"# BEGIN CI WASMTIME RELEASE PATCH\n"
f"{patch_lines}\n"
"# END CI WASMTIME RELEASE PATCH",
s,
count=1,
)

cargo_toml.write_text(s, encoding="utf-8")
PY
env:
WASMTIME_RELEASE_BRANCH: ${{ steps.wasmtime_release_branch.outputs.branch }}

- name: Prepare dependencies
if: steps.tested_cache.outputs.cache-hit != 'true'
run: ./y.sh prepare

- name: Build (sysroot none)
if: steps.tested_cache.outputs.cache-hit != 'true'
run: ./y.sh build --sysroot none

- name: Test
if: steps.tested_cache.outputs.cache-hit != 'true'
env:
TARGET_TRIPLE: x86_64-unknown-linux-gnu
run: ./y.sh test

106 changes: 93 additions & 13 deletions src/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use cranelift_codegen::ir::{
};
use cranelift_codegen::isa::CallConv;
use cranelift_module::ModuleError;
use rustc_abi::{CanonAbi, ExternAbi, X86Call};
use rustc_abi::{Align, CanonAbi, ExternAbi, X86Call};
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
Expand Down Expand Up @@ -243,10 +243,17 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_
self::returning::codegen_return_param(fx, &ssa_analyzed, &mut block_params_iter);
assert_eq!(fx.local_map.push(ret_place), RETURN_PLACE);

// None means pass_mode == NoPass
struct ArgValue<'tcx> {
value: Option<CValue<'tcx>>,
/// If set, the argument is a byval/byref pointer whose pointee alignment is smaller than
/// the Rust ABI alignment. In that case the argument must be copied to a sufficiently
/// aligned local stack slot before it can be treated as a place.
underaligned_pointee_align: Option<Align>,
}

enum ArgKind<'tcx> {
Normal(Option<CValue<'tcx>>),
Spread(Vec<Option<CValue<'tcx>>>),
Normal(ArgValue<'tcx>),
Spread(Vec<ArgValue<'tcx>>),
}

// FIXME implement variadics in cranelift
Expand Down Expand Up @@ -280,17 +287,29 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_
let mut params = Vec::new();
for (i, _arg_ty) in tupled_arg_tys.iter().enumerate() {
let arg_abi = arg_abis_iter.next().unwrap();
let param =
let value =
cvalue_for_param(fx, Some(local), Some(i), arg_abi, &mut block_params_iter);
params.push(param);
let underaligned_pointee_align = match arg_abi.mode {
PassMode::Indirect { attrs, .. } => attrs
.pointee_align
.filter(|&pointee_align| pointee_align < arg_abi.layout.align.abi),
_ => None,
};
params.push(ArgValue { value, underaligned_pointee_align });
}

(local, ArgKind::Spread(params), arg_ty)
} else {
let arg_abi = arg_abis_iter.next().unwrap();
let param =
let value =
cvalue_for_param(fx, Some(local), None, arg_abi, &mut block_params_iter);
(local, ArgKind::Normal(param), arg_ty)
let underaligned_pointee_align = match arg_abi.mode {
PassMode::Indirect { attrs, .. } => attrs
.pointee_align
.filter(|&pointee_align| pointee_align < arg_abi.layout.align.abi),
_ => None,
};
(local, ArgKind::Normal(ArgValue { value, underaligned_pointee_align }), arg_ty)
}
})
.collect::<Vec<(Local, ArgKind<'tcx>, Ty<'tcx>)>>();
Expand All @@ -311,7 +330,9 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_
for (local, arg_kind, ty) in func_params {
// While this is normally an optimization to prevent an unnecessary copy when an argument is
// not mutated by the current function, this is necessary to support unsized arguments.
if let ArgKind::Normal(Some(val)) = arg_kind {
if let ArgKind::Normal(ArgValue { value: Some(val), underaligned_pointee_align: None }) =
arg_kind
{
if let Some((addr, meta)) = val.try_to_ptr() {
// Ownership of the value at the backing storage for an argument is passed to the
// callee per the ABI, so it is fine to borrow the backing storage of this argument
Expand All @@ -336,15 +357,74 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_
assert_eq!(fx.local_map.push(place), local);

match arg_kind {
ArgKind::Normal(param) => {
if let Some(param) = param {
ArgKind::Normal(ArgValue { value: Some(param), underaligned_pointee_align }) => {
if let Some(pointee_align) = underaligned_pointee_align
&& let Some(dst_ptr) = place.try_to_ptr()
&& let Some((src_ptr, None)) = param.try_to_ptr()
&& layout.size != Size::ZERO
{
let mut flags = MemFlags::new();
flags.set_notrap();

let to_addr = dst_ptr.get_addr(fx);
let from_addr = src_ptr.get_addr(fx);
let size = layout.size.bytes();

// `emit_small_memory_copy` uses `u8` for alignments, just use the maximum
// alignment that fits in a `u8` if the actual alignment is larger.
let dst_align = layout.align.bytes().try_into().unwrap_or(128);
let src_align = pointee_align.bytes().try_into().unwrap_or(128);

fx.bcx.emit_small_memory_copy(
fx.target_config,
to_addr,
from_addr,
size,
dst_align,
src_align,
true,
flags,
);
} else {
place.write_cvalue(fx, param);
}
}
ArgKind::Normal(ArgValue { value: None, underaligned_pointee_align: _ }) => {}
ArgKind::Spread(params) => {
for (i, param) in params.into_iter().enumerate() {
for (i, ArgValue { value: param, underaligned_pointee_align }) in
params.into_iter().enumerate()
{
if let Some(param) = param {
place.place_field(fx, FieldIdx::new(i)).write_cvalue(fx, param);
let field_place = place.place_field(fx, FieldIdx::new(i));
let field_layout = field_place.layout();
if let Some(pointee_align) = underaligned_pointee_align
&& let Some(dst_ptr) = field_place.try_to_ptr()
&& let Some((src_ptr, None)) = param.try_to_ptr()
&& field_layout.size != Size::ZERO
{
let mut flags = MemFlags::new();
flags.set_notrap();

let to_addr = dst_ptr.get_addr(fx);
let from_addr = src_ptr.get_addr(fx);
let size = field_layout.size.bytes();

let dst_align = field_layout.align.bytes().try_into().unwrap_or(128);
let src_align = pointee_align.bytes().try_into().unwrap_or(128);

fx.bcx.emit_small_memory_copy(
fx.target_config,
to_addr,
from_addr,
size,
dst_align,
src_align,
true,
flags,
);
} else {
field_place.write_cvalue(fx, param);
}
}
}
}
Expand Down
Loading