]> granicus.if.org Git - clang/commitdiff
fix rdar://8360877 a really nasty miscompilation in Boost.Xpressive
authorChris Lattner <sabre@nondot.org>
Wed, 1 Sep 2010 00:50:20 +0000 (00:50 +0000)
committerChris Lattner <sabre@nondot.org>
Wed, 1 Sep 2010 00:50:20 +0000 (00:50 +0000)
caused by my ABI work.  Passing:

struct outer {
  int x;
  struct epsilon_matcher {} e;
  int f;
};

as {i32,i32} isn't safe, because the offset of the second element
needs to be at 8 when it is interpreted as a memory value.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@112686 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/TargetInfo.cpp
test/CodeGenCXX/x86_64-arguments.cpp

index 389fa110fab25a001062f2bb37b8d026346cdc46..4d221d4e657ed7999a10c76affa4127084791488 100644 (file)
@@ -1426,6 +1426,50 @@ GetINTEGERTypeAtOffset(const llvm::Type *IRType, unsigned IROffset,
                                 std::min(TySizeInBytes-SourceOffset, 8U)*8);
 }
 
+
+/// GetX86_64ByValArgumentPair - Given a high and low type that can ideally
+/// be used as elements of a two register pair to pass or return, return a
+/// first class aggregate to represent them.  For example, if the low part of
+/// a by-value argument should be passed as i32* and the high part as float,
+/// return {i32*, float}.
+static const llvm::Type *
+GetX86_64ByValArgumentPair(const llvm::Type *Lo, const llvm::Type *Hi,
+                           const llvm::TargetData &TD) {
+  // In order to correctly satisfy the ABI, we need to the high part to start
+  // at offset 8.  If the high and low parts we inferred are both 4-byte types
+  // (e.g. i32 and i32) then the resultant struct type ({i32,i32}) won't have
+  // the second element at offset 8.  Check for this:
+  unsigned LoSize = (unsigned)TD.getTypeAllocSize(Lo);
+  unsigned HiAlign = TD.getABITypeAlignment(Hi);
+  unsigned HiStart = llvm::TargetData::RoundUpAlignment(LoSize, HiAlign);
+  assert(HiStart != 0 && HiStart <= 8 && "Invalid x86-64 argument pair!");
+  
+  // To handle this, we have to increase the size of the low part so that the
+  // second element will start at an 8 byte offset.  We can't increase the size
+  // of the second element because it might make us access off the end of the
+  // struct.
+  if (HiStart != 8) {
+    // There are only two sorts of types the ABI generation code can produce for
+    // the low part of a pair that aren't 8 bytes in size: float or i8/i16/i32.
+    // Promote these to a larger type.
+    if (Lo->isFloatTy())
+      Lo = llvm::Type::getDoubleTy(Lo->getContext());
+    else {
+      assert(Lo->isIntegerTy() && "Invalid/unknown lo type");
+      Lo = llvm::Type::getInt64Ty(Lo->getContext());
+    }
+  }
+  
+  const llvm::StructType *Result = 
+    llvm::StructType::get(Lo->getContext(), Lo, Hi, NULL);
+  
+  
+  // Verify that the second element is at an 8-byte offset.
+  assert(TD.getStructLayout(Result)->getElementOffset(1) == 8 &&
+         "Invalid x86-64 argument pair!");
+  return Result;
+}
+
 ABIArgInfo X86_64ABIInfo::
 classifyReturnType(QualType RetTy) const {
   // AMD64-ABI 3.2.3p4: Rule 1. Classify the return type with the
@@ -1552,8 +1596,8 @@ classifyReturnType(QualType RetTy) const {
   // If a high part was specified, merge it together with the low part.  It is
   // known to pass in the high eightbyte of the result.  We do this by forming a
   // first class struct aggregate with the high and low part: {low, high}
-  if (HighPart) 
-    ResType = llvm::StructType::get(getVMContext(), ResType, HighPart, NULL);
+  if (HighPart)
+    ResType = GetX86_64ByValArgumentPair(ResType, HighPart, getTargetData());
 
   return ABIArgInfo::getDirect(ResType);
 }
@@ -1674,7 +1718,7 @@ ABIArgInfo X86_64ABIInfo::classifyArgumentType(QualType Ty, unsigned &neededInt,
   // known to pass in the high eightbyte of the result.  We do this by forming a
   // first class struct aggregate with the high and low part: {low, high}
   if (HighPart)
-    ResType = llvm::StructType::get(getVMContext(), ResType, HighPart, NULL);
+    ResType = GetX86_64ByValArgumentPair(ResType, HighPart, getTargetData());
   
   return ABIArgInfo::getDirect(ResType);
 }
index cd097680d370ccc8e6ccfee5b142c7bcc30628fd..e7316989feaa1400a346987122b354b4ff709a2a 100644 (file)
@@ -100,3 +100,18 @@ namespace test5 {
   // CHECK: alloca %"struct.test5::X"
   // CHECK: alloca %"struct.test5::Y"
 }
+
+
+// rdar://8360877
+namespace test6 {
+  struct outer {
+    int x;
+    struct epsilon_matcher {} e;
+    int f;
+  };
+
+  int test(outer x) {
+    return x.x + x.f;
+  }
+  // CHECK: define i32 @_ZN5test64testENS_5outerE(i64 %x.coerce0, i32 %x.coerce1)
+}