[llvm] 5c7b43a - [clang][AArch32] Correctly align HA arguments when passed on the stack

Momchil Velikov via llvm-commits llvm-commits at lists.llvm.org
Mon May 10 08:29:10 PDT 2021


Author: Momchil Velikov
Date: 2021-05-10T16:28:46+01:00
New Revision: 5c7b43aa8298a389b906d72c792941a0ce57782e

URL: https://github.com/llvm/llvm-project/commit/5c7b43aa8298a389b906d72c792941a0ce57782e
DIFF: https://github.com/llvm/llvm-project/commit/5c7b43aa8298a389b906d72c792941a0ce57782e.diff

LOG: [clang][AArch32] Correctly align HA arguments when passed on the stack

Analogously to https://reviews.llvm.org/D98794 this patch uses the
`alignstack` attribute to fix incorrect passing of homogeneous
aggregate (HA) arguments on AArch32. The EABI/AAPCS was recently
updated to clarify how VFP co-processor candidates are aligned:
https://github.com/ARM-software/abi-aa/commit/4488e34998514dc7af5507236f279f6881eede62

Differential Revision: https://reviews.llvm.org/D100853

Added: 
    clang/test/CodeGen/arm-ha-alignstack.c
    llvm/test/CodeGen/ARM/ha-alignstack-call.ll
    llvm/test/CodeGen/ARM/ha-alignstack.ll

Modified: 
    clang/lib/CodeGen/TargetInfo.cpp
    llvm/lib/Target/ARM/ARMCallingConv.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp
index 633b963965ed6..af516bbc8d312 100644
--- a/clang/lib/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CodeGen/TargetInfo.cpp
@@ -6440,7 +6440,16 @@ ABIArgInfo ARMABIInfo::classifyHomogeneousAggregate(QualType Ty,
       return ABIArgInfo::getDirect(Ty, 0, nullptr, false);
     }
   }
-  return ABIArgInfo::getDirect(nullptr, 0, nullptr, false);
+  unsigned Align = 0;
+  if (getABIKind() == ARMABIInfo::AAPCS ||
+      getABIKind() == ARMABIInfo::AAPCS_VFP) {
+    // For alignment adjusted HFAs, cap the argument alignment to 8, leave it
+    // default otherwise.
+    Align = getContext().getTypeUnadjustedAlignInChars(Ty).getQuantity();
+    unsigned BaseAlign = getContext().getTypeAlignInChars(Base).getQuantity();
+    Align = (Align > BaseAlign && Align >= 8) ? 8 : 0;
+  }
+  return ABIArgInfo::getDirect(nullptr, 0, nullptr, false, Align);
 }
 
 ABIArgInfo ARMABIInfo::classifyArgumentType(QualType Ty, bool isVariadic,

diff  --git a/clang/test/CodeGen/arm-ha-alignstack.c b/clang/test/CodeGen/arm-ha-alignstack.c
new file mode 100644
index 0000000000000..a4f7e4af2f13a
--- /dev/null
+++ b/clang/test/CodeGen/arm-ha-alignstack.c
@@ -0,0 +1,114 @@
+// RUN: %clang_cc1 -triple armv7-eabi                                    -emit-llvm %s -o - | \
+// RUN:     FileCheck %s --check-prefixes=CHECK,CHECK-SOFT
+// RUN: %clang_cc1 -triple armv7-eabi -target-abi aapcs -mfloat-abi hard -emit-llvm %s -o - | \
+// RUN:    FileCheck %s --check-prefixes=CHECK,CHECK-HARD
+// REQUIRES: arm-registered-target
+
+// CHECK: %struct.S0 = type { [4 x float] }
+// CHECK: %struct.S1 = type { [2 x float] }
+// CHECK: %struct.S2 = type { [4 x float] }
+// CHECK: %struct.D0 = type { [2 x double] }
+// CHECK: %struct.D1 = type { [2 x double] }
+// CHECK: %struct.D2 = type { [4 x double] }
+
+typedef struct {
+  float v[4];
+} S0;
+
+float f0(S0 s) {
+// CHECK-SOFT: define{{.*}} float @f0([4 x i32]  %s.coerce)
+// CHECK-HARD: define{{.*}} arm_aapcs_vfpcc float @f0(%struct.S0 %s.coerce)
+  return s.v[0];
+}
+
+float f0call() {
+  S0 s = {0.0f, };
+  return f0(s);
+// CHECK-SOFT: call float @f0([4 x i32]
+// CHECK-HARD: call arm_aapcs_vfpcc float @f0(%struct.S0
+}
+
+typedef struct {
+  __attribute__((aligned(8))) float v[2];
+} S1;
+
+float f1(S1 s) {
+// CHECK-SOFT: define{{.*}} float @f1([1 x i64]
+// CHECK-HARD: define{{.*}} arm_aapcs_vfpcc float @f1(%struct.S1 alignstack(8)
+  return s.v[0];
+}
+
+float f1call() {
+  S1 s = {0.0f, };
+  return f1(s);
+// CHECK-SOFT: call float @f1([1 x i64
+// CHECK-HARD: call arm_aapcs_vfpcc float @f1(%struct.S1 alignstack(8)
+}
+
+typedef struct {
+  __attribute__((aligned(16))) float v[4];
+} S2;
+
+float f2(S2 s) {
+// CHECK-SOFT: define{{.*}} float @f2([2 x i64]
+// CHECK-HARD: define{{.*}} arm_aapcs_vfpcc float @f2(%struct.S2 alignstack(8)
+  return s.v[0];
+}
+
+float f2call() {
+  S2 s = {0.0f, };
+  return f2(s);
+// CHECK-SOFT: call float @f2([2 x i64]
+// CHECK-HARD: call arm_aapcs_vfpcc float @f2(%struct.S2 alignstack(8)
+}
+
+typedef struct {
+  double v[2];
+} D0;
+
+double g0(D0 d) {
+// CHECK-SOFT: define{{.*}} double @g0([2 x i64]
+// CHECK-HARD: define{{.*}} arm_aapcs_vfpcc double @g0(%struct.D0 %d.coerce
+  return d.v[0];
+}
+
+double g0call() {
+  D0 d = {0.0, };
+  return g0(d);
+// CHECK-SOFT: call double @g0([2 x i64]
+// CHECK-HARD: call arm_aapcs_vfpcc double @g0(%struct.D0 %1
+}
+
+typedef struct {
+  __attribute__((aligned(16))) double v[2];
+} D1;
+
+double g1(D1 d) {
+// CHECK-SOFT: define{{.*}} double @g1([2 x i64]
+// CHECK-HARD: define{{.*}} arm_aapcs_vfpcc double @g1(%struct.D1 alignstack(8)
+  return d.v[0];
+}
+
+double g1call() {
+  D1 d = {0.0, };
+  return g1(d);
+// CHECK-SOFT: call double @g1([2 x i64]
+// CHECK-HARD: call arm_aapcs_vfpcc double @g1(%struct.D1 alignstack(8)
+}
+
+typedef struct {
+  __attribute__((aligned(32))) double v[4];
+} D2;
+
+double g2(D2 d) {
+// CHECK-SOFT: define{{.*}} double @g2([4 x i64]
+// CHECK-HARD: define{{.*}} arm_aapcs_vfpcc double @g2(%struct.D2 alignstack(8)
+  return d.v[0];
+}
+
+double g2call() {
+  D2 d = {0.0, };
+  return g2(d);
+// CHECK-SOFT: call double @g2([4 x i64]
+// CHECK-HARD: call arm_aapcs_vfpcc double @g2(%struct.D2 alignstack(8)
+}

diff  --git a/llvm/lib/Target/ARM/ARMCallingConv.cpp b/llvm/lib/Target/ARM/ARMCallingConv.cpp
index 67c822a5b6ef7..d8d9ca3b912fa 100644
--- a/llvm/lib/Target/ARM/ARMCallingConv.cpp
+++ b/llvm/lib/Target/ARM/ARMCallingConv.cpp
@@ -256,22 +256,26 @@ static bool CC_ARM_AAPCS_Custom_Aggregate(unsigned ValNo, MVT ValVT,
     }
     PendingMembers.clear();
     return true;
-  } else if (LocVT != MVT::i32)
+  }
+
+  if (LocVT != MVT::i32)
     RegList = SRegList;
 
   // Mark all regs as unavailable (AAPCS rule C.2.vfp for VFP, C.6 for core)
   for (auto Reg : RegList)
     State.AllocateReg(Reg);
 
+  // Clamp the alignment between 4 and 8.
+  if (State.getMachineFunction().getSubtarget<ARMSubtarget>().isTargetAEABI())
+    Alignment = ArgFlags.getNonZeroMemAlign() <= 4 ? Align(4) : Align(8);
+
   // After the first item has been allocated, the rest are packed as tightly as
   // possible. (E.g. an incoming i64 would have starting Align of 8, but we'll
   // be allocating a bunch of i32 slots).
-  const Align RestAlign = std::min(Alignment, Align(Size));
-
   for (auto &It : PendingMembers) {
     It.convertToMem(State.AllocateStack(Size, Alignment));
     State.addLoc(It);
-    Alignment = RestAlign;
+    Alignment = Align(1);
   }
 
   // All pending members have now been allocated

diff  --git a/llvm/test/CodeGen/ARM/ha-alignstack-call.ll b/llvm/test/CodeGen/ARM/ha-alignstack-call.ll
new file mode 100644
index 0000000000000..678c987cf3d4f
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/ha-alignstack-call.ll
@@ -0,0 +1,343 @@
+; RUN: llc --mtriple=armv7-eabihf %s -o - | FileCheck %s --enable-var-scope 
+
+%struct.S0 = type { [4 x float] }
+%struct.S1 = type { [2 x float] }
+%struct.S2 = type { [4 x float] }
+%struct.D0 = type { [2 x double] }
+%struct.D1 = type { [2 x double] }
+%struct.D2 = type { [4 x double] }
+
+; pass in regs
+declare dso_local float @f0_0(double, double, double, double, double, double, %struct.S0) local_unnamed_addr #0
+define dso_local float @f0_0_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call float @f0_0(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, %struct.S0 { [4 x float] [float 0x3FE3333340000000, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00] }) #0
+  ret float %call
+}
+; CHECK-LABEL: f0_0_call:
+; CHECK:       vldr s12, .L[[L:.*]]
+; CHECK:       b    f0_0
+; CHECK:      .L[[L]]:
+; CHECK-NEXT: .long 0x3f19999a
+
+; pass in memory, no split
+declare dso_local float @f0_1(double, double, double, double, double, double, float, %struct.S0) local_unnamed_addr #0
+define dso_local float @f0_1_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call float @f0_1(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, float 0x3FE3333340000000, %struct.S0 { [4 x float] [float 0x3FE6666660000000, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00] }) #0
+  ret float %call
+}
+; CHECK-LABEL: f0_1_call:
+; CHECK:       movw r1, #13107
+; CHECK:       mov  r0, #0
+; CHECK:       movt r1, #16179
+; CHECK-DAG:   str  r1, [sp]
+; CHECK-DAG:   str  r0, [sp, #4]
+; CHECK-DAG:   str  r0, [sp, #8]
+; CHECK-DAG:   str  r0, [sp, #12]
+; CHECK:       bl   f0_1
+
+; pass memory, alignment 4
+declare dso_local float @f0_2(double, double, double, double, double, double, double, double, float, %struct.S0) local_unnamed_addr #0
+define dso_local float @f0_2_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call float @f0_2(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, double 6.000000e-01, double 0x3FE6666666666666, float 0x3FE99999A0000000, %struct.S0 { [4 x float] [float 0x3FECCCCCC0000000, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00] }) #0
+  ret float %call
+}
+; CHECK-LABEL: f0_2_call:
+; CHECK:       movw r1, #26214
+; CHECK:       movw r2, #52429
+; CHECK:       mov  r0, #0
+; CHECK:       movt r1, #16230
+; CHECK:       movt r2, #16204
+; CHECK-DAG:   str  r2, [sp]
+; CHECK-DAG:   str  r1, [sp, #4]
+; CHECK-DAG:   str  r0, [sp, #8]
+; CHECK-DAG:   str  r0, [sp, #12]
+; CHECK-DAG:   str  r0, [sp, #16]
+; CHECK:       bl       f0_2
+
+; pass in regs
+declare dso_local float @f1_0(double, double, double, double, double, double, double, %struct.S1 alignstack(8)) local_unnamed_addr #0
+define dso_local float @f1_0_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call float @f1_0(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, double 6.000000e-01, %struct.S1 alignstack(8) { [2 x float] [float 0x3FE6666660000000, float 0.000000e+00] }) #0
+  ret float %call
+}
+; CHECK-LABEL: f1_0_call:
+; CHECK-DAG:   vldr s14, .L[[L0:.*]]
+; CHECK-DAG:   vldr s15, .L[[L1:.*]]
+; CHECK:       b    f1_0
+; CHECK:       .L[[L0]]:
+; CHECK-NEXT:  .long 0x3f333333
+; CHECK:       .L[[L1:.*]]:
+; CHECK-NEXT:  .long 0x00000000
+
+; pass in memory, no split
+declare dso_local float @f1_1(double, double, double, double, double, double, double, float, %struct.S1 alignstack(8)) local_unnamed_addr #0
+define dso_local float @f1_1_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call float @f1_1(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, double 6.000000e-01, float 0x3FE6666660000000, %struct.S1 alignstack(8) { [2 x float] [float 0x3FE99999A0000000, float 0.000000e+00] }) #0
+  ret float %call
+}
+; CHECK-LABEL: f1_1_call:
+; CHECK:       movw r1, #52429
+; CHECK:       mov  r0, #0
+; CHECK:       movt r1, #16204
+; CHECK-DAG:   str  r1, [sp]
+; CHECK-DAG:   str  r0, [sp, #4]
+; CHECK:       bl   f1_1
+
+; pass in memory, alignment 8
+declare dso_local float @f1_2(double, double, double, double, double, double, double, double, float, %struct.S1 alignstack(8)) local_unnamed_addr #0
+define dso_local float @f1_2_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call float @f1_2(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, double 6.000000e-01, double 0x3FE6666666666666, float 0x3FE99999A0000000, %struct.S1 alignstack(8) { [2 x float] [float 0x3FECCCCCC0000000, float 0.000000e+00] }) #0
+  ret float %call
+}
+; CHECK-LABEL: f1_2_call:
+; CHECK-DAG:   mov   r0, #0
+; CHECK-DAG:   movw  r1, #26214
+; CHECK:       str   r0, [sp, #12]
+; CHECK:       movw  r0, #52429
+; CHECK:       movt  r1, #16230
+; CHECK:       movt  r0, #16204
+; CHECK-DAG:   str   r1, [sp, #8]
+; CHECK-DAG:   str   r0, [sp]
+; CHECK:       bl    f1_2
+
+
+; pass in registers
+declare dso_local float @f2_0(double, double, double, double, double, double, %struct.S2 alignstack(8)) local_unnamed_addr #0
+define dso_local float @f2_0_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call float @f2_0(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, %struct.S2 alignstack(8) { [4 x float] [float 0x3FE3333340000000, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00] }) #0
+  ret float %call
+}
+; CHECK-LABEL: f2_0_call:
+; CHECK-DAG:   vldr     s12, .L[[L0:.*]]
+; CHECK-DAG:   vldr     s13, .L[[L1:.*]]
+; CHECK-DAG:   vmov.f32 s14, s13
+; CHECK-DAG:   vmov.f32 s15, s13
+; CHECK:       b        f2_0
+; CHECK:       .L[[L0]]:
+; CHECK-NEXT:  .long 0x3f19999a
+; CHECK:       .L[[L1]]:
+; CHECK-NEXT:  .long 0x00000000
+
+; pass in memory, no split
+declare dso_local float @f2_1(double, double, double, double, double, double, float, %struct.S2 alignstack(8)) local_unnamed_addr #0
+define dso_local float @f2_1_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call float @f2_1(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, float 0x3FE3333340000000, %struct.S2 alignstack(8) { [4 x float] [float 0x3FE6666660000000, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00] }) #0
+  ret float %call
+}
+; CHECK-LABEL: f2_1_call:
+; CHECK:       movw  r1, #13107
+; CHECK:       mov   r0, #0
+; CHECK:       movt  r1, #16179
+; CHECK:       str   r1, [sp]
+; CHECK:       str   r0, [sp, #4]
+; CHECK:       vldr  s12, .L[[L:.*]]
+; CHECK:       str   r0, [sp, #8]
+; CHECK:       str   r0, [sp, #12]
+; CHECK:       bl    f2_1
+; CHECK:       .L[[L]]:
+; CHECK-NEXT:  .long    0x3f19999a
+
+; pass in memory, alignment 8
+declare dso_local float @f2_2(double, double, double, double, double, double, double, double, float, %struct.S2 alignstack(8)) local_unnamed_addr #0
+define dso_local float @f2_2_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call float @f2_2(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, double 6.000000e-01, double 0x3FE6666666666666, float 0x3FE99999A0000000, %struct.S2 alignstack(8) { [4 x float] [float 0x3FECCCCCC0000000, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00] }) #0
+  ret float %call
+}
+; CHECK-LABEL: f2_2_call:
+; CHECK:       mov  r0, #0
+; CHECK:       movw r1, #26214
+; CHECK:       str  r0, [sp, #12]
+; CHECK:       str  r0, [sp, #16]
+; CHECK:       movt r1, #16230
+; CHECK:       str  r0, [sp, #20]
+; CHECK:       movw r0, #52429
+; CHECK:       movt r0, #16204
+; CHECK:       str  r1, [sp, #8]
+; CHECK:       str  r0, [sp]
+; CHECK:       bl   f2_2
+
+; pass in registers
+declare dso_local double @g0_0(double, double, double, double, double, double, %struct.D0) local_unnamed_addr #0
+define dso_local double @g0_0_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call double @g0_0(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, %struct.D0 { [2 x double] [double 6.000000e-01, double 0.000000e+00] }) #0
+  ret double %call
+}
+; CHECK-LABEL: g0_0_call:
+; CHECK:       vldr    d6, .L[[L:.*]]
+; CHECK:       b    g0_0
+; CHECK:       .L[[L]]
+; CHECK-NEXT:  long 858993459
+; CHECK-NEXT:  long 1071854387
+
+; pass in memory, no split
+declare dso_local double @g0_1(double, double, double, double, double, double, double, %struct.D0) local_unnamed_addr #0
+define dso_local double @g0_1_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call double @g0_1(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, double 6.000000e-01, %struct.D0 { [2 x double] [double 0x3FE6666666666666, double 0.000000e+00] }) #0
+  ret double %call
+}
+; CHECK-LABEL: g0_1_call:
+; CHECK:       movw  r0, #26214
+; CHECK:       movw  r1, #26214
+; CHECK:       mov   r2, #0
+; CHECK:       movt  r0, #16358
+; CHECK:       movt  r1, #26214
+; CHECK:       str   r1, [sp]
+; CHECK:       stmib sp, {r0, r2}
+; CHECK:       str   r2, [sp, #12]
+; CHECK:       bl    g0_1
+
+; pass in memory, alignment 8
+declare dso_local double @g0_2(double, double, double, double, double, double, double, double, float, %struct.D0) local_unnamed_addr #0
+define dso_local double @g0_2_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call double @g0_2(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, double 6.000000e-01, double 0x3FE6666666666666, float 0x3FE99999A0000000, %struct.D0 { [2 x double] [double 9.000000e-01, double 0.000000e+00] }) #0
+  ret double %call
+}
+; CHECK-LABEL: g0_2_call:
+; CHECK:       movw r0, #52428
+; CHECK:       movt r0, #16364
+; CHECK:       movw r1, #52429
+; CHECK:       str  r0, [sp, #12]
+; CHECK:       movw r0, #52429
+; CHECK:       mov  r2, #0
+; CHECK:       movt r1, #52428
+; CHECK:       movt r0, #16204
+; CHECK:       str  r1, [sp, #8]
+; CHECK:       str  r2, [sp, #16]
+; CHECK:       str  r2, [sp, #20]
+; CHECK:       str  r0, [sp]
+; CHECK:       bl   g0_2
+
+; pass in registers
+declare dso_local double @g1_0(double, double, double, double, double, double, %struct.D1 alignstack(8)) local_unnamed_addr #0
+define dso_local double @g1_0_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call double @g1_0(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, %struct.D1 alignstack(8) { [2 x double] [double 6.000000e-01, double 0.000000e+00] }) #0
+  ret double %call
+}
+; CHECK-LABEL: g1_0_call:
+; CHECK-DAG:   vmov.i32 d7, #0x0
+; CHECK-DAG:   vldr     d6, .L[[L:.*]]
+; CHECK:       b        g1_0
+; CHECK:      .L[[L]]:
+; CHECK-NEXT: .long    858993459
+; CHECK-NEXT: .long    107185438
+
+; pass in memory, no split
+declare dso_local double @g1_1(double, double, double, double, double, double, double, %struct.D1 alignstack(8)) local_unnamed_addr #0
+define dso_local double @g1_1_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call double @g1_1(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, double 6.000000e-01, %struct.D1 alignstack(8) { [2 x double] [double 0x3FE6666666666666, double 0.000000e+00] }) #0
+  ret double %call
+}
+; CHECK-LABEL: g1_1_call:
+; CHECK:       movw  r0, #26214
+; CHECK:       movw  r1, #26214
+; CHECK:       mov   r2, #0
+; CHECK:       movt  r0, #16358
+; CHECK:       movt  r1, #26214
+; CHECK:       str   r1, [sp]
+; CHECK:       stmib sp, {r0, r2}
+; CHECK:       str   r2, [sp, #12]
+; CHECK:       bl    g1_1
+
+; pass in memory, alignment 8
+declare dso_local double @g1_2(double, double, double, double, double, double, double, double, float, %struct.D1 alignstack(8)) local_unnamed_addr #0
+define dso_local double @g1_2_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call double @g1_2(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, double 6.000000e-01, double 0x3FE6666666666666, float 0x3FE99999A0000000, %struct.D1 alignstack(8) { [2 x double] [double 9.000000e-01, double 0.000000e+00] }) #0
+  ret double %call
+}
+; CHECK-LABEL: g1_2_call:
+; CHECK:       movw r0, #52428
+; CHECK:       movt r0, #16364
+; CHECK:       movw r1, #52429
+; CHECK:       str  r0, [sp, #12]
+; CHECK:       movw r0, #52429
+; CHECK:       mov  r2, #0
+; CHECK:       movt r1, #52428
+; CHECK:       movt r0, #16204
+; CHECK:       str  r1, [sp, #8]
+; CHECK:       str  r2, [sp, #16]
+; CHECK:       str  r2, [sp, #20]
+; CHECK:       str  r0, [sp]
+; CHECK:       bl   g1_2
+
+; pass in registers
+declare dso_local double @g2_0(double, double, double, double, %struct.D2 alignstack(8)) local_unnamed_addr #0
+define dso_local double @g2_0_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call double @g2_0(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, %struct.D2 alignstack(8) { [4 x double] [double 4.000000e-01, double 0.000000e+00, double 0.000000e+00, double 0.000000e+00] }) #0
+  ret double %call
+}
+; CHECK-LABEL: g2_0_call:
+; CHECK-DAG:   vldr     d4, .L[[L:.*]]
+; CHECK-DAG:   vmov.i32 d5, #0x0
+; CHECK-DAG:   vmov.i32 d6, #0x0
+; CHECK-DAG:   vmov.i32 d7, #0x0
+; CHECK:       b        g2_0
+; CHECK:       .L[[L]]:
+; CHECK-NEXT:  .long    2576980378
+; CHECK-NEXT:  .long    1071225241
+
+; pass in memory, no split
+; [sp] [sp + 4] =  0x00000000 0x3fe00000 = .5
+; [sp + 8] [sp + 12] = 0 0 = .0
+; ...
+declare dso_local double @g2_1(double, double, double, double, double, %struct.D2 alignstack(8)) local_unnamed_addr #0
+define dso_local double @g2_1_call() local_unnamed_addr #0 {
+entry:
+  %call = tail call double @g2_1(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, %struct.D2 alignstack(8) { [4 x double] [double 5.000000e-01, double 0.000000e+00, double 0.000000e+00, double 0.000000e+00] }) #0
+  ret double %call
+}
+; CHECK-LABEL: g2_1_call:
+; CHECK:       movw   r0, #0
+; CHECK:       mov    r1, #0
+; CHECK:       movt   r0, #16352
+; CHECK:       str    r1, [sp]
+; CHECK:       stmib  sp, {r0, r1}
+; CHECK:       str    r1, [sp, #12]
+; CHECK:       str    r1, [sp, #16]
+; CHECK:       str    r1, [sp, #20]
+; CHECK:       str    r1, [sp, #24]
+; CHECK:       str    r1, [sp, #28]
+; CHECK:       bl    g2_1
+
+; pass in memory, alignment 8
+declare dso_local double @g2_2(double, double, double, double, double, double, double, double, float, %struct.D2 alignstack(8)) local_unnamed_addr #0
+define dso_local double @g2_2call() local_unnamed_addr #0 {
+entry:
+  %call = tail call double @g2_2(double 0.000000e+00, double 1.000000e-01, double 2.000000e-01, double 3.000000e-01, double 4.000000e-01, double 5.000000e-01, double 6.000000e-01, double 0x3FE6666666666666, float 0x3FE99999A0000000, %struct.D2 alignstack(8) { [4 x double] [double 9.000000e-01, double 0.000000e+00, double 0.000000e+00, double 0.000000e+00] }) #0
+  ret double %call
+}
+; CHECK-LABEL: g2_2call:
+; CHECK:       movw r0, #52428
+; CHECK:       movt r0, #16364
+; CHECK:       movw r1, #52429
+; CHECK:       str  r0, [sp, #12]
+; CHECK:       movw r0, #52429
+; CHECK:       mov  r2, #0
+; CHECK:       movt r1, #52428
+; CHECK:       movt r0, #16204
+; CHECK:       str  r1, [sp, #8]
+; CHECK:       str  r2, [sp, #16]
+; CHECK:       str  r2, [sp, #20]
+; CHECK:       str  r2, [sp, #24]
+; CHECK:       str  r2, [sp, #28]
+; CHECK:       str  r2, [sp, #32]
+; CHECK:       str  r2, [sp, #36]
+; CHECK:       str  r0, [sp]
+; CHECK:       bl   g2_2
+
+attributes #0 = { nounwind "target-cpu"="generic" "target-features"="+armv7-a,+d32,+dsp,+fp64,+neon,+strict-align,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-thumb-mode" }

diff  --git a/llvm/test/CodeGen/ARM/ha-alignstack.ll b/llvm/test/CodeGen/ARM/ha-alignstack.ll
new file mode 100644
index 0000000000000..e6fab00a60912
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/ha-alignstack.ll
@@ -0,0 +1,190 @@
+; RUN: llc --mtriple armv7-eabihf %s -o - | FileCheck %s
+
+%struct.S0 = type { [4 x float] }
+%struct.S1 = type { [2 x float] }
+%struct.S2 = type { [4 x float] }
+%struct.D0 = type { [2 x double] }
+%struct.D1 = type { [2 x double] }
+%struct.D2 = type { [4 x double] }
+
+; pass in registers
+define dso_local float @f0_0(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, %struct.S0 %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.S0 %s.coerce, 0, 0
+  ret float %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: f0_0:
+; CHECK:       vmov.f32 s0, s12
+; CHECK-NEXT:  bx       lr
+
+; pass in memory, no memory/regs split
+define dso_local float @f0_1(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, float %x, %struct.S0 %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.S0 %s.coerce, 0, 0
+  ret float %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: f0_1:
+; CHECK:       vldr s0, [sp]
+; CHECK-NEXT:  bx   lr
+
+; pass in memory, alignment 4
+define dso_local float @f0_2(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, double %d7, float %x, %struct.S0 %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.S0 %s.coerce, 0, 0
+  ret float %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: f0_2:
+; CHECK:       vldr s0, [sp, #4]
+; CHECK-NEXT:  bx   lr
+
+; pass in registers
+define dso_local float @f1_0(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, %struct.S1 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.S1 %s.coerce, 0, 0
+  ret float %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: f1_0:
+; CHECK:       vmov.f32 s0, s14
+; CHECK-NEXT:  bx       lr
+
+; pass in memory, no memory/regs split
+define dso_local float @f1_1(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, float %x, %struct.S1 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.S1 %s.coerce, 0, 0
+  ret float %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: f1_1:
+; CHECK:       vldr s0, [sp]
+; CHECK-NEXT:  bx   lr
+
+; pass in memory, alignment 8
+define dso_local float @f1_2(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, double %d7, float %x, %struct.S1 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.S1 %s.coerce, 0, 0
+  ret float %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: f1_2:
+; CHECK:       vldr s0, [sp, #8]
+; CHECK-NEXT:  bx   lr
+
+; pass in registers
+define dso_local float @f2_0(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, %struct.S2 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.S2 %s.coerce, 0, 0
+  ret float %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: f2_0:
+; CHECK:       vmov.f32 s0, s12
+; CHECK-NEXT:  bx       lr
+
+; pass in memory, no memory/regs split
+define dso_local float @f2_1(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, float %x, %struct.S2 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.S2 %s.coerce, 0, 0
+  ret float %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: f2_1:
+; CHECK:       vldr s0, [sp]
+; CHECK-NEXT:  bx   lr
+
+; pass in memory, alignment 8
+define dso_local float @f2_2(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, double %d7, float %x, %struct.S2 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.S2 %s.coerce, 0, 0
+  ret float %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: f2_2:
+; CHECK:       vldr s0, [sp, #8]
+; CHECK-NEXT:  bx   lr
+
+; pass in registers
+define dso_local double @g0_0(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, %struct.D0 %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.D0 %s.coerce, 0, 0
+  ret double %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: g0_0:
+; CHECK:       vmov.f64 d0, d6
+; CHECK-NEXT:  bx       lr
+
+; pass in memory, no memory/regs split
+define dso_local double @g0_1(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, %struct.D0 %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.D0 %s.coerce, 0, 0
+  ret double %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: g0_1:
+; CHECK:       vldr d0, [sp]
+; CHECK-NEXT:  bx   lr
+
+; pass in memory, alignment 8
+define dso_local double @g0_2(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, double %d7, float %x, %struct.D0 %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.D0 %s.coerce, 0, 0
+  ret double %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: g0_2:
+; CHECK:       vldr d0, [sp, #8]
+; CHECK-NEXT:  bx   lr
+
+; pass in registers
+define dso_local double @g1_0(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, %struct.D1 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.D1 %s.coerce, 0, 0
+  ret double %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: g1_0:
+; CHECK:       vmov.f64 d0, d6
+; CHECK-NEXT:  bx       lr
+
+; pass in memory, no memory/regs split
+define dso_local double @g1_1(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, %struct.D1 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.D1 %s.coerce, 0, 0
+  ret double %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: g1_1:
+; CHECK:       vldr d0, [sp]
+; CHECK-NEXT:  bx   lr
+
+; pass in memory, alignment 8
+define dso_local double @g1_2(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, double %d7, float %x, %struct.D1 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.D1 %s.coerce, 0, 0
+  ret double %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: g1_2:
+; CHECK:       vldr d0, [sp, #8]
+; CHECK-NEXT:  bx   lr
+
+; pass in registers
+define dso_local double @g2_0(double %d0, double %d1, double %d2, double %d3, %struct.D2 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.D2 %s.coerce, 0, 0
+  ret double %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: g2_0:
+; CHECK:       vmov.f64 d0, d4
+; CHECK-NEXT:  bx       lr
+
+; pass in memory, no memory/regs split
+define dso_local double @g2_1(double %d0, double %d1, double %d2, double %d3, double %d4, %struct.D2 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.D2 %s.coerce, 0, 0
+  ret double %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: g2_1:
+; CHECK:       vldr d0, [sp]
+; CHECK-NEXT:  bx   lr
+
+; pass in memory, alignment 8
+define dso_local double @g2_2(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, double %d7, float %x, %struct.D2 alignstack(8) %s.coerce) local_unnamed_addr #0 {
+entry:
+  %s.coerce.fca.0.0.extract = extractvalue %struct.D2 %s.coerce, 0, 0
+  ret double %s.coerce.fca.0.0.extract
+}
+; CHECK-LABEL: g2_2:
+; CHECK:       vldr d0, [sp, #8]
+; CHECK-NEXT:  bx   lr
+
+attributes #0 = { "target-cpu"="generic" "target-features"="+armv7-a,+d32,+dsp,+fp64,+neon,+strict-align,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-thumb-mode" }


        


More information about the llvm-commits mailing list