Skip to content

Commit

Permalink
Start fixing struct sizing
Browse files Browse the repository at this point in the history
  • Loading branch information
yorickpeterse committed Nov 26, 2024
1 parent 93a9d1b commit 46844c5
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 87 deletions.
34 changes: 31 additions & 3 deletions compiler/src/llvm/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,35 @@ impl Context {
}
}

pub(crate) fn argument_type<'ctx>(
&'ctx self,
state: &State,
layouts: &Layouts<'ctx>,
typ: BasicTypeEnum<'ctx>,
) -> BasicTypeEnum<'ctx> {
// TODO: on AMD64 we can pass around structs <= 16 bytes as-is, but only
// if there are only two fields. On ARM64 we need to round them up to
// one or two pairs of i64 values:
//
// | Arch | Original | Result
// |-------|-------------------|-----------------
// | AMD64 | { i64, i32 } | { i64, i32 }
// | AMD64 | { i64, i32, i32 } | ptr
// | ARM64 | { i64, i32 } | { i64, i64 }
// | ARM64 | { i64, i32, i32 } | ptr (I think?)
//
// For anything larger than 16 bytes, pass as a pointer.
let BasicTypeEnum::StructType(typ) = typ else { return typ };
let max = state.config.target.pass_struct_size();
let size = layouts.target_data.get_abi_size(&typ);

if size > max {
self.pointer_type().as_basic_type_enum()
} else {
typ.as_basic_type_enum()
}
}

pub(crate) fn method_return_type<'ctx>(
&'ctx self,
state: &State,
Expand Down Expand Up @@ -238,15 +267,14 @@ impl Context {
// registers (if small enough), or using a pointer. LLVM doesn't
// detect when this is needed for us, so sadly we (and everybody
// else using LLVM) have to do this ourselves.
if typ.is_struct_type() {
let typ = typ.into_struct_type();

if let BasicTypeEnum::StructType(typ) = typ {
if layouts.target_data.get_abi_size(&typ)
> state.config.target.pass_struct_size()
{
args.push(self.pointer_type().into());
sret = Some(typ);
} else {
// TODO: pass as [2 x i64] when necessary
ret = Some(typ.as_basic_type_enum());
}
} else {
Expand Down
64 changes: 13 additions & 51 deletions compiler/src/llvm/layouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,11 +351,6 @@ impl<'ctx> Layouts<'ctx> {
) {
let db = &state.db;

// TODO: make reusable
let struct_max = state.config.target.pass_struct_size();
let struct_as_ptr =
state.config.target.pass_struct_arguments_as_pointers();

for calls in mir.dynamic_calls.values() {
for (method, _) in calls {
let mut args: Vec<BasicMetadataTypeEnum> = Vec::new();
Expand All @@ -366,17 +361,10 @@ impl<'ctx> Layouts<'ctx> {
.iter()
.chain(method.argument_types(db))
{
// TODO: make reusable
let typ = context.llvm_type(db, self, typ);
let raw = context.llvm_type(db, self, typ);
let typ = context.argument_type(state, self, raw);

if struct_as_ptr
&& typ.is_struct_type()
&& self.target_data.get_abi_size(&typ) > struct_max
{
args.push(context.pointer_type().into());
} else {
args.push(typ.into());
}
args.push(typ.into());
}

let signature =
Expand All @@ -401,11 +389,6 @@ impl<'ctx> Layouts<'ctx> {
) {
let db = &state.db;

// TODO: make reusable
let struct_max = state.config.target.pass_struct_size();
let struct_as_ptr =
state.config.target.pass_struct_arguments_as_pointers();

for mir_class in mir.classes.values() {
// Define the method signatures once (so we can cheaply retrieve
// them whenever needed), and assign the methods to their method
Expand All @@ -431,17 +414,10 @@ impl<'ctx> Layouts<'ctx> {
.iter()
.chain(method.argument_types(db))
{
// TODO: make reusable
let typ = context.llvm_type(db, self, typ);

if struct_as_ptr
&& typ.is_struct_type()
&& self.target_data.get_abi_size(&typ) > struct_max
{
args.push(context.pointer_type().into());
} else {
args.push(typ.into());
}
let raw = context.llvm_type(db, self, typ);
let typ = context.argument_type(state, self, raw);

args.push(typ.into());
}

(
Expand All @@ -466,17 +442,10 @@ impl<'ctx> Layouts<'ctx> {
context.method_return_type(state, self, method, &mut args);

for &typ in method.argument_types(db) {
let typ = context.llvm_type(db, self, typ);
let raw = context.llvm_type(db, self, typ);
let typ = context.argument_type(state, self, raw);

// TODO: make reusable
if struct_as_ptr
&& typ.is_struct_type()
&& self.target_data.get_abi_size(&typ) > struct_max
{
args.push(context.pointer_type().into());
} else {
args.push(typ.into());
}
args.push(typ.into());
}

let typ = ret
Expand All @@ -497,17 +466,10 @@ impl<'ctx> Layouts<'ctx> {
context.method_return_type(state, self, method, &mut args);

for &typ in method.argument_types(db) {
let typ = context.llvm_type(db, self, typ);
let raw = context.llvm_type(db, self, typ);
let typ = context.argument_type(state, self, raw);

// TODO: make reusable
if struct_as_ptr
&& typ.is_struct_type()
&& self.target_data.get_abi_size(&typ) > struct_max
{
args.push(context.pointer_type().into());
} else {
args.push(typ.into());
}
args.push(typ.into());
}

let variadic = method.is_variadic(db);
Expand Down
37 changes: 10 additions & 27 deletions compiler/src/llvm/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1966,29 +1966,15 @@ impl<'shared, 'module, 'ctx> LowerMethod<'shared, 'module, 'ctx> {
self.register_type(ins.register),
);

// TODO: make reusable
let struct_max =
self.shared.state.config.target.pass_struct_size();
let struct_as_ptr = self
.shared
.state
.config
.target
.pass_struct_arguments_as_pointers();

for reg in [ins.receiver].iter().chain(ins.arguments.iter()) {
let typ = self.variable_types[reg];

if struct_as_ptr
&& typ.is_struct_type()
&& self.layouts.target_data.get_abi_size(&typ)
> struct_max
{
fn_args
.push(self.builder.context.pointer_type().into());
} else {
fn_args.push(typ.into());
}
let raw = self.variable_types[reg];
let typ = self.builder.context.argument_type(
self.shared.state,
self.layouts,
raw,
);

fn_args.push(typ.into());
}

// Load the method from the method table.
Expand Down Expand Up @@ -2613,17 +2599,14 @@ impl<'shared, 'module, 'ctx> LowerMethod<'shared, 'module, 'ctx> {
None
};

// TODO: make reusable
let struct_max = self.shared.state.config.target.pass_struct_size();
let struct_as_ptr =
self.shared.state.config.target.pass_struct_arguments_as_pointers();

for reg in receiver.iter().chain(arguments.iter()) {
let typ = self.variable_types[reg];
let var = self.variables[reg];

if struct_as_ptr
&& typ.is_struct_type()
// TODO: cast to [2 x i64] when necessary
if typ.is_struct_type()
&& self.layouts.target_data.get_abi_size(&typ) > struct_max
{
args.push(var.into());
Expand Down
6 changes: 0 additions & 6 deletions compiler/src/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,6 @@ impl Target {
16
}

/// Returns `true` if large struct arguments should be passed by reference
/// instead of by value.
pub(crate) fn pass_struct_arguments_as_pointers(&self) -> bool {
matches!(self.arch, Architecture::Arm64)
}

pub(crate) fn stack_pointer_register_name(&self) -> &str {
match self.arch {
Architecture::Amd64 => "rsp",
Expand Down

0 comments on commit 46844c5

Please sign in to comment.