Skip to content
Merged
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
28 changes: 0 additions & 28 deletions .cspell.json

This file was deleted.

7 changes: 0 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,6 @@ jobs:
- uses: actions/checkout@v4
- run: python tools/check_intrinsics_duplicates.py

spell_check:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: crate-ci/typos@v1.32.0
- uses: streetsidesoftware/cspell-action@v7

build_system:
runs-on: ubuntu-24.04
steps:
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ dependencies = [

[[package]]
name = "gccjit"
version = "4.0.0"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be5dafc4e649cb4a363e95a5960ef50b0c6f1b8e136ff8eb2e928b40353b5d8b"
checksum = "859af1dd2815fd0f8ca97f5917a595f18c415692b07e58993a6ad34ff13204d5"
dependencies = [
"gccjit_sys",
]
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ default = ["master"]
[dependencies]
object = { version = "0.37.0", default-features = false, features = ["std", "read"] }
tempfile = "3.20"
gccjit = { version = "4.0.0", features = ["dlopen"] }
gccjit = { version = "4.1.0", features = ["dlopen"] }
#gccjit = { git = "https://github.com/rust-lang/gccjit.rs", branch = "error-dlopen", features = ["dlopen"] }

# Local copy.
Expand Down
3 changes: 3 additions & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
disallowed-methods = [
{ path = "gccjit::types::Type::add_attribute", reason = "go through `type_::apply_struct_attributes` instead: an attribute set directly on a type would not be part of the `CodegenCx::struct_types` cache key, so it would silently change every other use of that type" },
]
2 changes: 1 addition & 1 deletion src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl GccType for CastTarget {
args.push(cx.type_ix(rem_bytes * 8));
}

cx.type_struct(&args, false)
cx.type_struct(&args, &[])
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rustc_session::PointerAuthSchema;

use crate::consts::const_alloc_to_gcc;
use crate::context::{CodegenCx, new_array_type};
use crate::type_::struct_attributes;
use crate::type_of::LayoutGccExt;

impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
Expand Down Expand Up @@ -288,7 +289,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
fn const_struct(&self, values: &[RValue<'gcc>], packed: bool) -> RValue<'gcc> {
let fields: Vec<_> = values.iter().map(|value| value.get_type()).collect();
// FIXME(antoyo): cache the type? It's anonymous, so probably not.
let typ = self.type_struct(&fields, packed);
let typ = self.type_struct(&fields, &struct_attributes(packed, None));
let struct_type = typ.is_struct().expect("struct type");
self.context.new_struct_constructor(None, struct_type.as_type(), None, values)
}
Expand Down
4 changes: 3 additions & 1 deletion src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, TlsModel, X86Abi};
use crate::abi::conv_to_fn_attribute;
use crate::callee::get_fn;
use crate::common::SignType;
use crate::type_::StructTypeKey;

#[cfg_attr(not(feature = "master"), expect(dead_code))]
pub struct CodegenCx<'gcc, 'tcx> {
Expand Down Expand Up @@ -85,7 +86,8 @@ pub struct CodegenCx<'gcc, 'tcx> {
pub types: RefCell<FxHashMap<(Ty<'tcx>, Option<VariantIdx>), Type<'gcc>>>,
pub tcx: TyCtxt<'tcx>,

pub struct_types: RefCell<FxHashMap<Vec<Type<'gcc>>, Type<'gcc>>>,
/// Cache of the anonymous struct types.
pub struct_types: RefCell<FxHashMap<StructTypeKey<'gcc>, Type<'gcc>>>,

/// Cache instances of monomorphic and polymorphic items
pub instances: RefCell<FxHashMap<Instance<'tcx>, LValue<'gcc>>>,
Expand Down
2 changes: 2 additions & 0 deletions src/gcc_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ pub fn new_context<'gcc>(sess: &Session) -> Context<'gcc> {
context.add_command_line_option("-fno-strict-aliasing");
// NOTE: Rust relies on LLVM doing wrapping on overflow.
context.add_command_line_option("-fwrapv");
// NOTE: This is needed to hide a warning caused by the alignment fix on byval arguments.
context.add_command_line_option("-Wno-psabi");

if let Some(model) = sess.code_model() {
use rustc_target::spec::CodeModel;
Expand Down
15 changes: 5 additions & 10 deletions src/intrinsic/llvm.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use std::borrow::Cow;

#[cfg(feature = "master")]
use gccjit::TypeAttribute;
use gccjit::{CType, Context, Field, Function, FunctionPtrType, RValue, ToRValue, Type};
use rustc_codegen_ssa::traits::BuilderMethods;

use crate::builder::Builder;
use crate::context::{CodegenCx, new_array_type};
use crate::type_::{StructAttribute, apply_struct_attributes};

fn encode_key_128_type<'a, 'gcc, 'tcx>(
builder: &Builder<'a, 'gcc, 'tcx>,
Expand All @@ -24,8 +23,7 @@ fn encode_key_128_type<'a, 'gcc, 'tcx>(
"EncodeKey128Output",
&[field1, field2, field3, field4, field5, field6, field7],
);
#[cfg(feature = "master")]
encode_type.as_type().add_attribute(TypeAttribute::Packed);
apply_struct_attributes(encode_type.as_type(), &[StructAttribute::Packed]);
(encode_type.as_type(), field1, field2)
}

Expand All @@ -46,8 +44,7 @@ fn encode_key_256_type<'a, 'gcc, 'tcx>(
"EncodeKey256Output",
&[field1, field2, field3, field4, field5, field6, field7, field8],
);
#[cfg(feature = "master")]
encode_type.as_type().add_attribute(TypeAttribute::Packed);
apply_struct_attributes(encode_type.as_type(), &[StructAttribute::Packed]);
(encode_type.as_type(), field1, field2)
}

Expand All @@ -59,8 +56,7 @@ fn aes_output_type<'a, 'gcc, 'tcx>(
let field2 = builder.context.new_field(None, m128i, "field2");
let aes_output_type = builder.context.new_struct_type(None, "AesOutput", &[field1, field2]);
let typ = aes_output_type.as_type();
#[cfg(feature = "master")]
typ.add_attribute(TypeAttribute::Packed);
apply_struct_attributes(typ, &[StructAttribute::Packed]);
(typ, field1, field2)
}

Expand All @@ -82,8 +78,7 @@ fn wide_aes_output_type<'a, 'gcc, 'tcx>(
"WideAesOutput",
&[field1, field2, field3, field4, field5, field6, field7, field8, field9],
);
#[cfg(feature = "master")]
aes_output_type.as_type().add_attribute(TypeAttribute::Packed);
apply_struct_attributes(aes_output_type.as_type(), &[StructAttribute::Packed]);
(aes_output_type.as_type(), field1, field2)
}

Expand Down
106 changes: 93 additions & 13 deletions src/type_.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#[cfg(feature = "master")]
use std::convert::TryInto;
use std::mem::discriminant;

#[cfg(feature = "master")]
use gccjit::{CType, TypeAttribute};
Expand Down Expand Up @@ -101,9 +102,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
self.bool_type
}

pub fn type_struct(&self, fields: &[Type<'gcc>], packed: bool) -> Type<'gcc> {
let types = fields.to_vec();
if let Some(typ) = self.struct_types.borrow().get(fields) {
pub fn type_struct(&self, fields: &[Type<'gcc>], attributes: &[StructAttribute]) -> Type<'gcc> {
let key =
StructTypeKey { fields: fields.to_vec(), attributes: canonical_attributes(attributes) };
if let Some(typ) = self.struct_types.borrow().get(&key) {
return *typ;
}
let fields: Vec<_> = fields
Expand All @@ -114,15 +116,91 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
})
.collect();
let typ = self.context.new_struct_type(None, "struct", &fields).as_type();
if packed {
#[cfg(feature = "master")]
typ.add_attribute(TypeAttribute::Packed);
}
self.struct_types.borrow_mut().insert(types, typ);
apply_struct_attributes(typ, &key.attributes);
self.struct_types.borrow_mut().insert(key, typ);
typ
}
}

/// An attribute that can be set on a GCC struct type.
///
/// This mirrors the subset of `gccjit::TypeAttribute` that cg_gcc needs, rather than using it
/// directly, because it must exist without the `master` feature and because it is what
/// `StructTypeKey` is keyed on. Adding a variant here is therefore all it takes to make a new
/// attribute part of the cache key: there is no second place to remember to update.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum StructAttribute {
/// Alignment, in bytes.
Aligned(u32),
/// Lay the fields out without inserting padding between them.
Packed,
}

/// Identifies an anonymous struct type in `CodegenCx::struct_types`.
///
/// Two Rust types with the same field list can still need distinct GCC types — `struct { a: u64,
/// b: u64 }` with and without `repr(align(16))` produces the same fields — so every attribute has
/// to be part of the key. Holding them as one [`StructAttribute`] list rather than as separate
/// fields is what keeps that true when a new attribute is added.
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct StructTypeKey<'gcc> {
pub fields: Vec<Type<'gcc>>,
pub attributes: Vec<StructAttribute>,
}

/// The attributes a GCC struct needs in order to match the Rust layout it is built from.
pub fn struct_attributes(packed: bool, align: Option<Align>) -> Vec<StructAttribute> {
let mut attributes = Vec::new();
if packed {
attributes.push(StructAttribute::Packed);
}
if let Some(align) = align
&& align.bytes() > 1
&& align.bytes() <= MAX_STRUCT_ALIGNMENT
{
attributes.push(StructAttribute::Aligned(align.bytes() as u32));
}
attributes
}

/// The largest alignment GCC accepts on a type, in bytes.
const MAX_STRUCT_ALIGNMENT: u64 = 1 << 28;

/// Put an attribute list into a canonical form so that it can be used as a cache key.
///
/// Without this, `[Packed, Aligned(8)]` and `[Aligned(8), Packed]` would hash differently and mint
/// two GCC types for what is one Rust type.
fn canonical_attributes(attributes: &[StructAttribute]) -> Vec<StructAttribute> {
let mut attributes = attributes.to_vec();
attributes.sort_unstable();
attributes.dedup();
debug_assert!(
attributes.windows(2).all(|pair| discriminant(&pair[0]) != discriminant(&pair[1])),
"contradictory struct attributes: {attributes:?}"
);
attributes
}

/// Set `attributes` on the struct type `typ`.
///
/// This is the only place allowed to call `Type::add_attribute`; `clippy.toml` forbids it
/// everywhere else. An attribute set on a type that `CodegenCx::struct_types` handed out would
/// change every other use of that type, so attributes have to be decided when the type is created
/// and be part of its cache key. Going through [`StructAttribute`] is what enforces that.
#[cfg(feature = "master")]
#[allow(clippy::disallowed_methods)]
pub fn apply_struct_attributes(typ: Type<'_>, attributes: &[StructAttribute]) {
for attribute in attributes {
typ.add_attribute(match *attribute {
StructAttribute::Aligned(align) => TypeAttribute::Aligned(align),
StructAttribute::Packed => TypeAttribute::Packed,
});
}
}

#[cfg(not(feature = "master"))]
pub fn apply_struct_attributes(_typ: Type<'_>, _attributes: &[StructAttribute]) {}

impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> {
fn type_i8(&self) -> Type<'gcc> {
self.i8_type
Expand Down Expand Up @@ -324,17 +402,19 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
self.type_array(self.type_from_integer(unit), size / unit_size)
}

pub fn set_struct_body(&self, typ: Struct<'gcc>, fields: &[Type<'gcc>], packed: bool) {
pub fn set_struct_body(
&self,
typ: Struct<'gcc>,
fields: &[Type<'gcc>],
attributes: &[StructAttribute],
) {
let fields: Vec<_> = fields
.iter()
.enumerate()
.map(|(index, field)| self.context.new_field(None, *field, format!("field_{}", index)))
.collect();
typ.set_fields(None, &fields);
if packed {
#[cfg(feature = "master")]
typ.as_type().add_attribute(TypeAttribute::Packed);
}
apply_struct_attributes(typ.as_type(), &canonical_attributes(attributes));
}

pub fn type_named_struct(&self, name: &str) -> Struct<'gcc> {
Expand Down
17 changes: 11 additions & 6 deletions src/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use rustc_target::callconv::{CastTarget, FnAbi};

use crate::abi::{FnAbiGcc, FnAbiGccExt, GccType};
use crate::context::CodegenCx;
use crate::type_::struct_fields;
use crate::type_::{struct_attributes, struct_fields};

impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
fn type_from_unsigned_integer(&self, i: Integer) -> Type<'gcc> {
Expand Down Expand Up @@ -81,7 +81,7 @@ fn uncached_gcc_type<'gcc, 'tcx>(
layout.scalar_pair_element_gcc_type(cx, 0),
layout.scalar_pair_element_gcc_type(cx, 1),
],
false,
&struct_attributes(false, Some(layout.align.abi)),
);
}
BackendRepr::Memory { .. } => {}
Expand Down Expand Up @@ -129,11 +129,12 @@ fn uncached_gcc_type<'gcc, 'tcx>(
FieldsShape::Primitive | FieldsShape::Union(_) => {
let fill = cx.type_padding_filler(layout.size, layout.align.abi);
let packed = false;
let attributes = struct_attributes(packed, Some(layout.align.abi));
match name {
None => cx.type_struct(&[fill], packed),
None => cx.type_struct(&[fill], &attributes),
Some(ref name) => {
let gcc_type = cx.type_named_struct(name);
cx.set_struct_body(gcc_type, &[fill], packed);
cx.set_struct_body(gcc_type, &[fill], &attributes);
gcc_type.as_type()
}
}
Expand All @@ -142,7 +143,7 @@ fn uncached_gcc_type<'gcc, 'tcx>(
FieldsShape::Arbitrary { .. } => match name {
None => {
let (gcc_fields, packed) = struct_fields(cx, layout);
cx.type_struct(&gcc_fields, packed)
cx.type_struct(&gcc_fields, &struct_attributes(packed, Some(layout.align.abi)))
}
Some(ref name) => {
let gcc_type = cx.type_named_struct(name);
Expand Down Expand Up @@ -240,7 +241,11 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {

if let Some((deferred_ty, layout)) = defer {
let (fields, packed) = struct_fields(cx, layout);
cx.set_struct_body(deferred_ty, &fields, packed);
cx.set_struct_body(
deferred_ty,
&fields,
&struct_attributes(packed, Some(layout.align.abi)),
);
}

ty
Expand Down
Loading
Loading