-
Notifications
You must be signed in to change notification settings - Fork 14.8k
[Arm64EC][clang] Implement varargs support in clang. #152411
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
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
---|---|---|
|
@@ -1409,6 +1409,12 @@ class WinX86_64ABIInfo : public ABIInfo { | |
return isX86VectorCallAggregateSmallEnough(NumMembers); | ||
} | ||
|
||
ABIArgInfo classifyArgForArm64ECVarArg(QualType Ty) const override { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why we do Arm64EC in X86.cpp? Should use a common name if it's shared? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. classifyArgForArm64ECVarArg is exclusively for use by Arm64EC calling convention code... but it's implemented in the x64 calling convention code because it's exactly the algorithm we've already implemented for x64, and I don't want to copy-paste it. I could refactor the code further to extract the underlying algorithm out of WinX86_64ABIInfo, but that doesn't seem like it would make the code more readable overall. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add some comments to explain this for anyone else who is wondering why there's Arm in their X86, I'm not sure that factoring out this code really helps with readability or maintainability. |
||
unsigned FreeSSERegs = 0; | ||
return classify(Ty, FreeSSERegs, /*IsReturnType=*/false, | ||
/*IsVectorCall=*/false, /*IsRegCall=*/false); | ||
} | ||
|
||
private: | ||
ABIArgInfo classify(QualType Ty, unsigned &FreeSSERegs, bool IsReturnType, | ||
bool IsVectorCall, bool IsRegCall) const; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals --include-generated-funcs --global-value-regex "f" | ||
// RUN: %clang_cc1 -opaque-pointers -triple arm64ec-windows-msvc -emit-llvm -o - %s | FileCheck %s | ||
|
||
typedef struct { float x[2]; } A; | ||
typedef struct { float x[4]; } B; | ||
void f(A a, ...) { | ||
__builtin_va_list b; | ||
__builtin_va_start(b, a); | ||
float x = __builtin_va_arg(b, A).x[0]; | ||
float y = __builtin_va_arg(b, B).x[0]; | ||
} | ||
void g(A a, B b) { f(a, b); } | ||
Comment on lines
+4
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to have a test with a size that isn’t a power of two. |
||
|
||
// CHECK-LABEL: @f( | ||
// CHECK-NEXT: entry: | ||
// CHECK-NEXT: [[A:%.*]] = alloca [[STRUCT_A:%.*]], align 4 | ||
// CHECK-NEXT: [[B:%.*]] = alloca ptr, align 8 | ||
// CHECK-NEXT: [[X:%.*]] = alloca float, align 4 | ||
// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [[STRUCT_A]], align 4 | ||
// CHECK-NEXT: [[Y:%.*]] = alloca float, align 4 | ||
// CHECK-NEXT: [[REF_TMP2:%.*]] = alloca [[STRUCT_B:%.*]], align 4 | ||
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_A]], ptr [[A]], i32 0, i32 0 | ||
// CHECK-NEXT: store i64 [[A_COERCE:%.*]], ptr [[COERCE_DIVE]], align 4 | ||
// CHECK-NEXT: call void @llvm.va_start.p0(ptr [[B]]) | ||
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[B]], align 8 | ||
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 | ||
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[B]], align 8 | ||
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[REF_TMP]], ptr align 8 [[ARGP_CUR]], i64 8, i1 false) | ||
// CHECK-NEXT: [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_A]], ptr [[REF_TMP]], i32 0, i32 0 | ||
// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x float], ptr [[X1]], i64 0, i64 0 | ||
// CHECK-NEXT: [[TMP0:%.*]] = load float, ptr [[ARRAYIDX]], align 4 | ||
// CHECK-NEXT: store float [[TMP0]], ptr [[X]], align 4 | ||
// CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[B]], align 8 | ||
// CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i64 8 | ||
// CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[B]], align 8 | ||
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR3]], align 8 | ||
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[REF_TMP2]], ptr align 4 [[TMP1]], i64 16, i1 false) | ||
// CHECK-NEXT: [[X5:%.*]] = getelementptr inbounds nuw [[STRUCT_B]], ptr [[REF_TMP2]], i32 0, i32 0 | ||
// CHECK-NEXT: [[ARRAYIDX6:%.*]] = getelementptr inbounds [4 x float], ptr [[X5]], i64 0, i64 0 | ||
// CHECK-NEXT: [[TMP2:%.*]] = load float, ptr [[ARRAYIDX6]], align 4 | ||
// CHECK-NEXT: store float [[TMP2]], ptr [[Y]], align 4 | ||
// CHECK-NEXT: ret void | ||
// | ||
// | ||
// CHECK-LABEL: @g( | ||
// CHECK-NEXT: entry: | ||
// CHECK-NEXT: [[A:%.*]] = alloca [[STRUCT_A:%.*]], align 4 | ||
// CHECK-NEXT: [[B:%.*]] = alloca [[STRUCT_B:%.*]], align 4 | ||
// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[STRUCT_B]], align 4 | ||
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_A]], ptr [[A]], i32 0, i32 0 | ||
// CHECK-NEXT: store [2 x float] [[A_COERCE:%.*]], ptr [[COERCE_DIVE]], align 4 | ||
// CHECK-NEXT: [[COERCE_DIVE1:%.*]] = getelementptr inbounds nuw [[STRUCT_B]], ptr [[B]], i32 0, i32 0 | ||
// CHECK-NEXT: store [4 x float] [[B_COERCE:%.*]], ptr [[COERCE_DIVE1]], align 4 | ||
// CHECK-NEXT: [[COERCE_DIVE2:%.*]] = getelementptr inbounds nuw [[STRUCT_A]], ptr [[A]], i32 0, i32 0 | ||
// CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[COERCE_DIVE2]], align 4 | ||
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[BYVAL_TEMP]], ptr align 4 [[B]], i64 16, i1 false) | ||
// CHECK-NEXT: call void (i64, ...) @f(i64 [[TMP0]], ptr dead_on_return noundef [[BYVAL_TEMP]]) | ||
// CHECK-NEXT: ret void | ||
// |
Uh oh!
There was an error while loading. Please reload this page.