Addressing 2nd round of feedback, enabling a Sema error, clang format and
some minor fixes.
Hi rnk, rsmith,
http://llvm-reviews.chandlerc.com/D1026
CHANGE SINCE LAST DIFF
http://llvm-reviews.chandlerc.com/D1026?vs=2536&id=2578#toc
Files:
include/clang/AST/ASTContext.h
include/clang/Sema/Sema.h
lib/AST/CMakeLists.txt
lib/AST/MicrosoftRecordLayoutBuilder.cpp
lib/AST/RecordLayoutBuilder.cpp
lib/Sema/SemaDecl.cpp
test/Sema/ms_bitfield_layout.c
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -1624,6 +1624,7 @@
/// record (struct/union/class) \p D, which indicates its size and field
/// position information.
const ASTRecordLayout &getASTRecordLayout(const RecordDecl *D) const;
+ const ASTRecordLayout *BuildMicrosoftASTRecordLayout(const RecordDecl *D) const;
/// \brief Get or compute information about the layout of the specified
/// Objective-C interface.
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -7267,8 +7267,8 @@
/// Returns false on success.
/// Can optionally return whether the bit-field is of width 0
ExprResult VerifyBitField(SourceLocation FieldLoc, IdentifierInfo *FieldName,
- QualType FieldTy, Expr *BitWidth,
- bool *ZeroWidth = 0);
+ QualType FieldTy, bool IsMsStruct,
+ Expr *BitWidth, bool *ZeroWidth = 0);
enum CUDAFunctionTarget {
CFT_Device,
Index: lib/AST/CMakeLists.txt
===================================================================
--- lib/AST/CMakeLists.txt
+++ lib/AST/CMakeLists.txt
@@ -39,6 +39,7 @@
Mangle.cpp
MicrosoftCXXABI.cpp
MicrosoftMangle.cpp
+ MicrosoftRecordLayoutBuilder.cpp
NestedNameSpecifier.cpp
NSAPI.cpp
ParentMap.cpp
Index: lib/AST/MicrosoftRecordLayoutBuilder.cpp
===================================================================
--- /dev/null
+++ lib/AST/MicrosoftRecordLayoutBuilder.cpp
@@ -0,0 +1,284 @@
+//=== MicrosoftRecordLayoutBuilder.cpp - Microsoft record layout helpers ---==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MathExtras.h"
+
+using namespace clang;
+
+namespace {
+struct MicrosoftRecordLayoutBuilder {
+ const ASTContext &Context;
+
+ MicrosoftRecordLayoutBuilder(const ASTContext &Context) : Context(Context) {}
+
+ MicrosoftRecordLayoutBuilder(const MicrosoftRecordLayoutBuilder &)
+ LLVM_DELETED_FUNCTION;
+ void operator=(const MicrosoftRecordLayoutBuilder &)LLVM_DELETED_FUNCTION;
+
+ void Layout(const RecordDecl *RD);
+ void InitializeLayout(const RecordDecl *RD);
+ void FinalizeLayout(const RecordDecl *RD);
+
+ void LayoutField(const FieldDecl *FD);
+ void LayoutBitField(const FieldDecl *FD);
+ void LayoutZeroWidthBitField(const FieldDecl *FD);
+
+ void UpdateAlignment(CharUnits NewAlignment);
+
+ /// \brief Gets the size and alignment taking attributes into account.
+ std::pair<CharUnits, CharUnits>
+ getAdjustedFieldInfo(const FieldDecl *FD) const;
+
+ void PlaceFieldAtZero() { FieldOffsets.push_back(0); }
+ void PlaceFieldAtOffset(CharUnits FieldOffset) {
+ FieldOffsets.push_back(Context.toBits(FieldOffset));
+ }
+ void PlaceFieldAtBitOffset(uint64_t FieldOffset) {
+ FieldOffsets.push_back(FieldOffset);
+ }
+
+ void WarnIgnoredBitfieldAndAlignment();
+ void WarnUnionedBitfieldAndAlignment();
+
+ // ------ Output Values ------
+ /// \brief The size of the record being laid out.
+ CharUnits Size;
+ /// \brief The current alignment of the record layout.
+ CharUnits Alignment;
+ /// \brief The collection of field offsets.
+ SmallVector<uint64_t, 16> FieldOffsets;
+
+ // ------ Layout Properties ------
+ bool IsUnion;
+ /// \brief The maximum allowed field alignment. This is set by #pragma pack.
+ CharUnits MaxFieldAlignment;
+
+ // ------ Intermediate state ------
+ // Bit-Field intermediate state
+ /// \brief True if the last field layed out was a bitfield and was not 0
+ /// width.
+ bool LastFieldIsNonZeroWidthBitfield;
+ /// \brief The size of the allocation of the currently active bitfield.
+ /// This value isn't meaningful unless LastFieldIsNonZeroWidthBitfield
+ /// is true.
+ CharUnits CurrentBitfieldSize;
+ /// \brief The number of remaining bits in our last bitfield allocation.
+ /// This value isn't meaningful unless LastFieldIsNonZeroWidthBitfield is
+ /// true.
+ unsigned RemainingBitsInField;
+};
+} // end anonymous namespace
+
+std::pair<CharUnits, CharUnits>
+MicrosoftRecordLayoutBuilder::getAdjustedFieldInfo(const FieldDecl *FD) const {
+ std::pair<CharUnits, CharUnits> FieldInfo;
+ if (FD->getType()->isIncompleteArrayType()) {
+ // This is a flexible array member; we can't directly
+ // query getTypeInfo about these, so we figure it out here.
+ // Flexible array members don't have any size, but they
+ // have to be aligned appropriately for their element type.
+ FieldInfo.first = CharUnits::Zero();
+ const ArrayType *ATy = Context.getAsArrayType(FD->getType());
+ FieldInfo.second = Context.getTypeAlignInChars(ATy->getElementType());
+ } else
+ FieldInfo = Context.getTypeInfoInChars(FD->getType());
+
+ // Respect alignment attributes.
+ FieldInfo.first = std::max(
+ FieldInfo.first, Context.toCharUnitsFromBits(FD->getMaxAlignment()));
+ FieldInfo.second = std::max(
+ FieldInfo.second, Context.toCharUnitsFromBits(FD->getMaxAlignment()));
+ // Respect packed attribute.
+ if (FD->hasAttr<PackedAttr>())
+ FieldInfo.second = CharUnits::One();
+ // Respect pack pragma.
+ else if (!MaxFieldAlignment.isZero())
+ FieldInfo.second = std::min(FieldInfo.second, MaxFieldAlignment);
+ return FieldInfo;
+}
+
+void MicrosoftRecordLayoutBuilder::UpdateAlignment(CharUnits NewAlignment) {
+ if (NewAlignment > Alignment) {
+ assert(llvm::isPowerOf2_32(NewAlignment.getQuantity() &&
+ "Alignment not a power of 2"));
+ Alignment = NewAlignment;
+ }
+}
+
+void MicrosoftRecordLayoutBuilder::InitializeLayout(const RecordDecl *RD) {
+ // Read layout properties
+ IsUnion = RD->isUnion();
+
+ // Initalize Output State.
+ Alignment = CharUnits::One();
+ Size = CharUnits::Zero();
+
+ // Clear our bit-fild related temporaries.
+ LastFieldIsNonZeroWidthBitfield = false;
+
+ // Honor the default struct packing maximum alignment flag.
+ MaxFieldAlignment = CharUnits::Zero();
+ if (unsigned DefaultMaxFieldAlignment = Context.getLangOpts().PackStruct)
+ MaxFieldAlignment = CharUnits::fromQuantity(DefaultMaxFieldAlignment);
+ if (const MaxFieldAlignmentAttr *MFAA = RD->getAttr<MaxFieldAlignmentAttr>())
+ MaxFieldAlignment = Context.toCharUnitsFromBits(MFAA->getAlignment());
+ // Packed attribute forces max field alignment to be 1.
+ if (RD->hasAttr<PackedAttr>())
+ MaxFieldAlignment = CharUnits::One();
+
+ // Set the alignment to the maximum alignment set by any attribute.
+ if (unsigned MaxAlign = RD->getMaxAlignment())
+ UpdateAlignment(Context.toCharUnitsFromBits(MaxAlign));
+}
+
+void MicrosoftRecordLayoutBuilder::FinalizeLayout(const RecordDecl *RD) {
+ Size = Size.RoundUpToAlignment(Alignment);
+}
+
+void MicrosoftRecordLayoutBuilder::Layout(const RecordDecl *RD) {
+ InitializeLayout(RD);
+ for (RecordDecl::field_iterator Field = RD->field_begin(),
+ FieldEnd = RD->field_end();
+ Field != FieldEnd; ++Field)
+ LayoutField(*Field);
+ FinalizeLayout(RD);
+}
+
+void MicrosoftRecordLayoutBuilder::LayoutField(const FieldDecl *FD) {
+ if (FD->isBitField()) {
+ LayoutBitField(FD);
+ return;
+ }
+
+ std::pair<CharUnits, CharUnits> FieldInfo = getAdjustedFieldInfo(FD);
+ CharUnits FieldSize = FieldInfo.first;
+ CharUnits FieldAlign = FieldInfo.second;
+
+ LastFieldIsNonZeroWidthBitfield = false;
+ UpdateAlignment(FieldAlign);
+ if (IsUnion) {
+ PlaceFieldAtZero();
+ Size = std::max(Size, FieldSize);
+ } else {
+ // Round up the current record size to the field's alignment boundary.
+ CharUnits FieldOffset = Size.RoundUpToAlignment(FieldAlign);
+ PlaceFieldAtOffset(FieldOffset);
+ Size = FieldOffset + FieldSize;
+ }
+}
+
+void MicrosoftRecordLayoutBuilder::LayoutBitField(const FieldDecl *FD) {
+ unsigned Width = FD->getBitWidthValue(Context);
+ if (Width == 0) {
+ LayoutZeroWidthBitField(FD);
+ return;
+ }
+
+ std::pair<CharUnits, CharUnits> FieldInfo = getAdjustedFieldInfo(FD);
+ CharUnits FieldSize = FieldInfo.first;
+ CharUnits FieldAlign = FieldInfo.second;
+
+ if (Width > Context.toBits(FieldSize)) {
+ // Clamp the bitfield to a containable size for the sake of being able
+ // to lay them out. Sema will error later.
+ Width = Context.toBits(FieldSize);
+ }
+
+ // Check to see if this bitfield fits into an existing allocation.
+ if (!IsUnion && LastFieldIsNonZeroWidthBitfield &&
+ CurrentBitfieldSize == FieldSize && Width <= RemainingBitsInField) {
+ PlaceFieldAtBitOffset(Context.toBits(Size) - RemainingBitsInField);
+ RemainingBitsInField -= Width;
+ return;
+ }
+
+ LastFieldIsNonZeroWidthBitfield = true;
+ CurrentBitfieldSize = FieldSize;
+ if (IsUnion) {
+ PlaceFieldAtZero();
+ Size = std::max(Size, FieldSize);
+ if (FieldAlign > Alignment)
+ WarnUnionedBitfieldAndAlignment();
+ } else {
+ // Allocate a new block of memory and place the bitfield in it.
+ CharUnits FieldOffset = Size.RoundUpToAlignment(FieldAlign);
+ PlaceFieldAtOffset(FieldOffset);
+ Size = FieldOffset + FieldSize;
+ UpdateAlignment(FieldAlign);
+ RemainingBitsInField = Context.toBits(FieldSize) - Width;
+ }
+}
+
+void
+MicrosoftRecordLayoutBuilder::LayoutZeroWidthBitField(const FieldDecl *FD) {
+ // Zero-width bitfields are ignored unless they follow a non-zero-width
+ // bitfield.
+ std::pair<CharUnits, CharUnits> FieldInfo = getAdjustedFieldInfo(FD);
+ CharUnits FieldSize = FieldInfo.first;
+ CharUnits FieldAlign = FieldInfo.second;
+
+ if (!LastFieldIsNonZeroWidthBitfield) {
+ PlaceFieldAtOffset(IsUnion ? CharUnits::Zero() : Size);
+ if (FieldAlign > Alignment)
+ WarnIgnoredBitfieldAndAlignment();
+ return;
+ }
+
+ LastFieldIsNonZeroWidthBitfield = false;
+ if (IsUnion) {
+ PlaceFieldAtZero();
+ Size = std::max(Size, FieldSize);
+ if (FieldAlign > Alignment)
+ WarnUnionedBitfieldAndAlignment();
+ } else {
+ // Round up the current record size to the field's alignment boundary.
+ CharUnits FieldOffset = Size.RoundUpToAlignment(FieldAlign);
+ PlaceFieldAtOffset(FieldOffset);
+ Size = FieldOffset;
+ UpdateAlignment(FieldAlign);
+ }
+}
+
+void MicrosoftRecordLayoutBuilder::WarnIgnoredBitfieldAndAlignment() {
+ // TODO(whunt) add the appropriate warning
+}
+
+void MicrosoftRecordLayoutBuilder::WarnUnionedBitfieldAndAlignment() {
+ // TODO(whunt) add the appropriate warning
+}
+
+/// \brief Get or compute information about the layout of the specified record
+/// (struct/union/class), which indicates its size and field position
+/// information.
+const ASTRecordLayout *
+ASTContext::BuildMicrosoftASTRecordLayout(const RecordDecl *D) const {
+ if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
+ assert(false && "we don't yet support C++ struct layout");
+ return 0;
+ } else {
+ MicrosoftRecordLayoutBuilder Builder(*this);
+ Builder.Layout(D);
+ return new (*this) ASTRecordLayout(
+ *this, Builder.Size, Builder.Alignment, Builder.Size,
+ Builder.FieldOffsets.data(), Builder.FieldOffsets.size());
+ }
+}
Index: lib/AST/RecordLayoutBuilder.cpp
===================================================================
--- lib/AST/RecordLayoutBuilder.cpp
+++ lib/AST/RecordLayoutBuilder.cpp
@@ -2524,15 +2524,21 @@
Builder.PrimaryBaseIsVirtual,
Builder.Bases, Builder.VBases);
} else {
- RecordLayoutBuilder Builder(*this, /*EmptySubobjects=*/0);
- Builder.Layout(D);
-
- NewEntry =
- new (*this) ASTRecordLayout(*this, Builder.getSize(),
- Builder.Alignment,
- Builder.getSize(),
- Builder.FieldOffsets.data(),
- Builder.FieldOffsets.size());
+ if (getTargetInfo().getTriple().getArch() == llvm::Triple::x86 &&
+ getTargetInfo().getTriple().getOS() == llvm::Triple::Win32 &&
+ !D->isMsStruct(*this))
+ NewEntry = BuildMicrosoftASTRecordLayout(D);
+ else {
+ RecordLayoutBuilder Builder(*this, /*EmptySubobjects=*/0);
+ Builder.Layout(D);
+
+ NewEntry =
+ new (*this) ASTRecordLayout(*this, Builder.getSize(),
+ Builder.Alignment,
+ Builder.getSize(),
+ Builder.FieldOffsets.data(),
+ Builder.FieldOffsets.size());
+ }
}
ASTRecordLayouts[D] = NewEntry;
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -10386,8 +10386,8 @@
// Note that FieldName may be null for anonymous bitfields.
ExprResult Sema::VerifyBitField(SourceLocation FieldLoc,
IdentifierInfo *FieldName,
- QualType FieldTy, Expr *BitWidth,
- bool *ZeroWidth) {
+ QualType FieldTy, bool IsMsStruct,
+ Expr *BitWidth, bool *ZeroWidth) {
// Default to true; that shouldn't confuse checks for emptiness
if (ZeroWidth)
*ZeroWidth = true;
@@ -10436,7 +10436,7 @@
if (!FieldTy->isDependentType()) {
uint64_t TypeSize = Context.getTypeSize(FieldTy);
if (Value.getZExtValue() > TypeSize) {
- if (!getLangOpts().CPlusPlus) {
+ if (!getLangOpts().CPlusPlus || IsMsStruct) {
if (FieldName)
return Diag(FieldLoc, diag::err_bitfield_width_exceeds_type_size)
<< FieldName << (unsigned)Value.getZExtValue()
@@ -10654,7 +10654,8 @@
bool ZeroWidth = false;
// If this is declared as a bit-field, check the bit-field.
if (!InvalidDecl && BitWidth) {
- BitWidth = VerifyBitField(Loc, II, T, BitWidth, &ZeroWidth).take();
+ BitWidth = VerifyBitField(Loc, II,
+ T, Record->isMsStruct(Context), BitWidth, &ZeroWidth).take();
if (!BitWidth) {
InvalidDecl = true;
BitWidth = 0;
@@ -10835,7 +10836,7 @@
if (BitWidth) {
// 6.7.2.1p3, 6.7.2.1p4
- BitWidth = VerifyBitField(Loc, II, T, BitWidth).take();
+ BitWidth = VerifyBitField(Loc, II, T, false, BitWidth).take();
if (!BitWidth)
D.setInvalidType();
} else {
Index: test/Sema/ms_bitfield_layout.c
===================================================================
--- /dev/null
+++ test/Sema/ms_bitfield_layout.c
@@ -0,0 +1,255 @@
+// RUN: %clang_cc1 -emit-llvm-only -triple i686-pc-win32 -fdump-record-layouts %s 2>&1 \
+// RUN: | FileCheck %s
+
+typedef struct A {
+ char x;
+ int a : 22;
+ int : 0;
+ int c : 10;
+ char b : 3;
+ char d: 4;
+ short y;
+} A;
+
+// CHECK: Type: struct A
+// CHECK: Size:128
+// CHECK: DataSize:128
+// CHECK: Alignment:32
+// CHECK: FieldOffsets: [0, 32, 64, 64, 96, 99, 112]>
+
+typedef struct B {
+ char x;
+ int : 0;
+ short a : 4;
+ char y;
+} B;
+
+// CHECK: Type: struct B
+// CHECK: Size:48
+// CHECK: DataSize:48
+// CHECK: Alignment:16
+// CHECK: FieldOffsets: [0, 8, 16, 32]>
+
+typedef struct C {
+ char x;
+ short a : 4;
+ int : 0;
+ char y;
+} C;
+
+// CHECK: Type: struct C
+// CHECK: Size:64
+// CHECK: DataSize:64
+// CHECK: Alignment:32
+// CHECK: FieldOffsets: [0, 16, 32, 32]>
+
+typedef struct D {
+ char x;
+ short : 0;
+ int : 0;
+ char y;
+} D;
+
+// CHECK: Type: struct D
+// CHECK: Size:16
+// CHECK: DataSize:16
+// CHECK: Alignment:8
+// CHECK: FieldOffsets: [0, 8, 8, 8]>
+
+typedef union E {
+ char x;
+ long long a : 3;
+ int b : 3;
+ long long : 0;
+ short y;
+} E;
+
+// CHECK: Type: union E
+// CHECK: Size:64
+// CHECK: DataSize:64
+// CHECK: Alignment:16
+// CHECK: FieldOffsets: [0, 0, 0, 0, 0]>
+
+typedef struct F {
+ char x;
+ char a : 3;
+ char b : 3;
+ char c : 3;
+ short d : 6;
+ short e : 6;
+ short f : 6;
+ short g : 11;
+ short h : 11;
+ short i : 11;
+ short y;
+} F;
+
+// CHECK: Type: struct F
+// CHECK: Size:128
+// CHECK: DataSize:128
+// CHECK: Alignment:16
+// CHECK: FieldOffsets: [0, 8, 11, 16, 32, 38, 48, 64, 80, 96, 112]>
+
+typedef union G {
+ char x;
+ int a : 3;
+ int : 0;
+ long long : 0;
+ short y;
+} G;
+
+// CHECK: Type: union G
+// CHECK: Size:32
+// CHECK: DataSize:32
+// CHECK: Alignment:16
+// CHECK: FieldOffsets: [0, 0, 0, 0, 0]>
+
+typedef struct H {
+ unsigned short a : 1;
+ unsigned char : 0;
+ unsigned long : 0;
+ unsigned short c : 1;
+} H;
+
+// CHECK: Type: struct H
+// CHECK: Size:32
+// CHECK: DataSize:32
+// CHECK: Alignment:16
+// CHECK: FieldOffsets: [0, 16, 16, 16]>
+
+#pragma pack(push, 1)
+
+typedef struct A1 {
+ char x;
+ int a : 22;
+ int : 0;
+ int c : 10;
+ char b : 3;
+ char d: 4;
+ short y;
+} A1;
+
+// CHECK: Type: struct A1
+// CHECK: Size:96
+// CHECK: DataSize:96
+// CHECK: Alignment:8
+// CHECK: FieldOffsets: [0, 8, 40, 40, 72, 75, 80]>
+
+typedef struct B1 {
+ char x;
+ int : 0;
+ short a : 4;
+ char y;
+} B1;
+
+// CHECK: Type: struct B1
+// CHECK: Size:32
+// CHECK: DataSize:32
+// CHECK: Alignment:8
+// CHECK: FieldOffsets: [0, 8, 8, 24]>
+
+typedef struct C1 {
+ char x;
+ short a : 4;
+ int : 0;
+ char y;
+} C1;
+
+// CHECK: Type: struct C1
+// CHECK: Size:32
+// CHECK: DataSize:32
+// CHECK: Alignment:8
+// CHECK: FieldOffsets: [0, 8, 24, 24]>
+
+typedef struct D1 {
+ char x;
+ short : 0;
+ int : 0;
+ char y;
+} D1;
+
+// CHECK: Type: struct D1
+// CHECK: Size:16
+// CHECK: DataSize:16
+// CHECK: Alignment:8
+// CHECK: FieldOffsets: [0, 8, 8, 8]>
+
+typedef union E1 {
+ char x;
+ long long a : 3;
+ int b : 3;
+ long long : 0;
+ short y;
+} E1;
+
+// CHECK: Type: union E1
+// CHECK: Size:64
+// CHECK: DataSize:64
+// CHECK: Alignment:8
+// CHECK: FieldOffsets: [0, 0, 0, 0, 0]>
+
+typedef struct F1 {
+ char x;
+ char a : 3;
+ char b : 3;
+ char c : 3;
+ short d : 6;
+ short e : 6;
+ short f : 6;
+ short g : 11;
+ short h : 11;
+ short i : 11;
+ short y;
+} F1;
+
+// CHECK: Type: struct F1
+// CHECK: Size:120
+// CHECK: DataSize:120
+// CHECK: Alignment:8
+// CHECK: FieldOffsets: [0, 8, 11, 16, 24, 30, 40, 56, 72, 88, 104]>
+
+typedef union G1 {
+ char x;
+ int a : 3;
+ int : 0;
+ long long : 0;
+ short y;
+} G1;
+
+// CHECK: Type: union G1
+// CHECK: Size:32
+// CHECK: DataSize:32
+// CHECK: Alignment:8
+// CHECK: FieldOffsets: [0, 0, 0, 0, 0]>
+
+typedef struct H1 {
+ unsigned long a : 1;
+ unsigned char : 0;
+ unsigned long : 0;
+ unsigned long c : 1;
+} H1;
+
+// CHECK: Type: struct H1
+// CHECK: Size:64
+// CHECK: DataSize:64
+// CHECK: Alignment:8
+// CHECK: FieldOffsets: [0, 32, 32, 32]>
+
+#pragma pack(pop)
+
+A a;
+B b;
+C c;
+D d;
+E e;
+F f;
+G g;
+H h;
+A1 a1;
+B1 b1;
+C1 c1;
+D1 d1;
+E1 e1;
+F1 f1;
+G1 g1;
+H1 h1;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits