-
Notifications
You must be signed in to change notification settings - Fork 15.5k
[clang][SPARC] Pass 16-aligned structs with the correct alignment in CC #155829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
a8707a6
05f7e2b
ba36452
fcfb1aa
fffdefb
60744a6
364730f
58fd500
a11d8ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,12 +25,35 @@ long double f_ld(long double x) { return x; } | |
| struct empty {}; | ||
| struct emptyarr { struct empty a[10]; }; | ||
|
|
||
| // In 16-byte structs, 16-byte aligned members are expanded | ||
| // to their corresponding i128/f128 types. | ||
| struct align16_int { _Alignas(16) int x; }; | ||
| struct align16_mixed { _Alignas(16) int x; double y; }; | ||
| struct align16_longdouble { long double x; }; | ||
|
|
||
| // CHECK-LABEL: define{{.*}} i64 @f_empty(i64 %x.coerce) | ||
| struct empty f_empty(struct empty x) { return x; } | ||
|
|
||
| // CHECK-LABEL: define{{.*}} i64 @f_emptyarr(i64 %x.coerce) | ||
| struct empty f_emptyarr(struct emptyarr x) { return x.a[0]; } | ||
|
|
||
| // CHECK-LABEL: define{{.*}} void @f_aligncaller(i64 %a.coerce0, i64 %a.coerce1) | ||
| // CHECK-LABEL: declare{{.*}} void @f_aligncallee(i32 noundef signext, i64, i64, i64) | ||
| void f_aligncallee(int pad, struct align16_int a); | ||
| void f_aligncaller(struct align16_int a) { | ||
| f_aligncallee(0, a); | ||
| } | ||
|
|
||
| // CHECK-LABEL: define{{.*}} double @f_mixed_aligned(i64 noundef %a, i64 %0, i64 %b.coerce0, double %b.coerce1) | ||
| double f_mixed_aligned(long a, struct align16_mixed b) { | ||
| return b.y; | ||
| } | ||
|
|
||
| // CHECK-LABEL: define{{.*}} fp128 @f_longdouble(i64 noundef %a, i64 %0, fp128 %b.coerce) | ||
| long double f_longdouble(long a, struct align16_longdouble b) { | ||
| return b.x; | ||
| } | ||
|
|
||
| // CHECK-LABEL: define{{.*}} i64 @f_emptyvar(i32 noundef zeroext %count, ...) | ||
| long f_emptyvar(unsigned count, ...) { | ||
| long ret; | ||
|
|
@@ -80,13 +103,25 @@ struct medium { | |
| int *c, *d; | ||
| }; | ||
|
|
||
| struct medium_aligned { | ||
| _Alignas(16) int *a; | ||
| int *b, *c, *d; | ||
| }; | ||
|
|
||
| // CHECK-LABEL: define{{.*}} %struct.medium @f_medium(ptr dead_on_return noundef %x) | ||
| struct medium f_medium(struct medium x) { | ||
| x.a += *x.b; | ||
| x.b = 0; | ||
| return x; | ||
| } | ||
|
|
||
| // CHECK-LABEL: define{{.*}} %struct.medium_aligned @f_medium_aligned(ptr dead_on_return noundef %x) | ||
| struct medium_aligned f_medium_aligned(struct medium_aligned x) { | ||
| x.a += *x.b; | ||
| x.b = 0; | ||
| return x; | ||
| } | ||
|
|
||
| // Large structs are also returned indirectly. | ||
| struct large { | ||
| int *a, *b; | ||
|
|
@@ -101,6 +136,15 @@ struct large f_large(struct large x) { | |
| return x; | ||
| } | ||
|
|
||
| // Large returns are converted into a pointer argument. | ||
| // Such conversion should preserve the alignment of overaligned arguments. | ||
| // define{{.*}} void @f_largereturn_aligned(ptr dead_on_unwind noalias writable sret(%struct.large) align 8 %agg.result, i64 %0, i64 %x.coerce0, i64 %x.coerce1) | ||
| struct large f_largereturn_aligned(struct align16_int x) { | ||
| struct large ret; | ||
| ret.x = x.x; | ||
| return ret; | ||
| } | ||
|
|
||
| // A 64-bit struct fits in a register. | ||
| struct reg { | ||
| int a, b; | ||
|
|
@@ -215,6 +259,17 @@ int f_variable(char *f, ...) { | |
| case 'm': | ||
| s += *va_arg(ap, struct medium).a; | ||
| break; | ||
|
|
||
| // CHECK: %[[CUR:[^ ]+]] = load ptr, ptr %ap | ||
| // CHECK-DAG: %[[NXT:[^ ]+]] = getelementptr inbounds i8, ptr %[[CUR]], i64 8 | ||
| // CHECK-DAG: store ptr %[[NXT]], ptr %ap | ||
| // CHECK-DAG: %[[ADR:[^ ]+]] = load ptr, ptr %[[CUR]] | ||
|
||
| // CHECK-DAG: call void @llvm.memcpy.p0.p0.i64(ptr align 16 {{.*}}, ptr align 16 %[[ADR]], i64 32, i1 false) | ||
| // CHECK: br | ||
| case 'M': | ||
| s += *va_arg(ap, struct medium_aligned).a; | ||
| break; | ||
| } | ||
|
|
||
| return s; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do ArgOffset and RetOffset have to be connected somehow? Returns can be lowered to an argument, which takes a register. Not sure if that register is an argument register in the SPARC calling convention; if it isn't, this is fine, I guess.
Are the padding rules the same on the stack as they are in registers, for functions that take many arguments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oof yeah, if a return is converted to a pointer argument it'll take an argument register...
So in that specific case the argument and return offsets should be connected, yes.
As for the latter, stack arguments have the same padding (or rather, alignment) requirements as they do in registers; there has to be a 1:1 correspondence between registers and stack memory locations.