// RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-runtime-has-weak -triple x86_64-apple-darwin -print-ivar-layout -emit-llvm -o /dev/null %s > %t-64.layout
// RUN: FileCheck -check-prefix CHECK-LP64 --input-file=%t-64.layout %s
// rdar://12184410
// rdar://12752901

void x(id y) {}
void y(int a) {}

extern id opaque_id();

void f() {
    __weak id wid;
    __block int byref_int = 0;
    char ch = 'a';
    char ch1 = 'b';
    char ch2 = 'c';
    short sh = 2;
    const id bar = (id) opaque_id();
    id baz = 0;
    __strong id strong_void_sta;
    __block id byref_bab = (id)0;
    __block id bl_var1;
    int i; double dob;

// The patterns here are a sequence of bytes, each saying first how
// many sizeof(void*) chunks to skip (high nibble) and then how many
// to scan (low nibble).  A zero byte says that we've reached the end
// of the pattern.
//
// All of these patterns start with 01 3x because the block header on
// LP64 consists of an isa pointer (which we're supposed to scan for
// some reason) followed by three words (2 ints, a function pointer,
// and a descriptor pointer).

// Test 1
// Inline instruction for block variable layout: 0x0320 (3 strong 2 byref)
// CHECK-LP64: Inline block variable layout: 0x0320, BL_STRONG:3, BL_BYREF:2, BL_OPERATOR:0
    void (^b)() = ^{
        byref_int = sh + ch+ch1+ch2 ;
        x(bar);
        x(baz);
        x((id)strong_void_sta);
        x(byref_bab);
    };    
    b();

// Test 2
// Inline instruction for block variable layout: 0x0331 (3 strong 3 byref 1 weak)
// CHECK-LP64: Inline block variable layout: 0x0331, BL_STRONG:3, BL_BYREF:3, BL_WEAK:1, BL_OPERATOR:0
    void (^c)() = ^{
        byref_int = sh + ch+ch1+ch2 ;
        x(bar);
        x(baz);
        x((id)strong_void_sta);
        x(wid);
        bl_var1 = 0;
        x(byref_bab);
    };    
}

@class NSString, NSNumber;
void g() {
  NSString *foo;
   NSNumber *bar;
   unsigned int bletch;
   __weak id weak_delegate;
  unsigned int i;
  NSString *y;
  NSString *z;
// Inline instruction for block variable layout: 0x0401 (4 strong 0 byref 1 weak)
// CHECK-LP64: Inline block variable layout: 0x0401, BL_STRONG:4, BL_WEAK:1, BL_OPERATOR:0
  void (^c)() = ^{
   int j = i + bletch;
   x(foo);
   x(bar);
   x(weak_delegate);
   x(y);
   x(z); 
  };
  c();
}

// Test 5 (unions/structs and their nesting):
void h() {
  struct S5 {
    int i1;
    __unsafe_unretained id o1;
    struct V {
     int i2;
     __unsafe_unretained id o2;
    } v1;
    int i3;
    union UI {
        void * i1;
        __unsafe_unretained id o1;
        int i3;
        __unsafe_unretained id o3;
    }ui;
  };

  union U {
        void * i1;
        __unsafe_unretained id o1;
        int i3;
        __unsafe_unretained id o3;
  }ui;

  struct S5 s2;
  union U u2;
  __block id block_id;

// CHECK-LP64: Block variable layout: BL_BYREF:1, BL_NON_OBJECT_WORD:1, BL_UNRETAINED:1, BL_NON_OBJECT_WORD:1, BL_UNRETAINED:1, BL_OPERATOR:0
  void (^c)() = ^{
    x(s2.ui.o1);
    x(u2.o1);
    block_id = 0;
  };
  c();
}

// Test for array of stuff.
void arr1() {
  struct S {
    __unsafe_unretained id unsafe_unretained_var[4];
 } imported_s;

// CHECK-LP64: Block variable layout: BL_UNRETAINED:4, BL_OPERATOR:0
    void (^c)() = ^{
        x(imported_s.unsafe_unretained_var[2]);
    };    

   c();
}

// Test2 for array of stuff.
void arr2() {
  struct S {
   int a;
    __unsafe_unretained id unsafe_unretained_var[4];
 } imported_s;

// CHECK-LP64: Block variable layout: BL_NON_OBJECT_WORD:1, BL_UNRETAINED:4, BL_OPERATOR:0
    void (^c)() = ^{
        x(imported_s.unsafe_unretained_var[2]);
    };    

   c();
}

// Test3 for array of stuff.
void arr3() {
  struct S {
   int a;
    __unsafe_unretained id unsafe_unretained_var[0];
 } imported_s;

// CHECK-LP64: Block variable layout: BL_OPERATOR:0
    void (^c)() = ^{
      int i = imported_s.a;
    };    

   c();
}


// Test4 for array of stuff.
@class B;
void arr4() {
  struct S {
    struct s0 {
      __unsafe_unretained id s_f0;
      __unsafe_unretained id s_f1;
    } f0;

    __unsafe_unretained id f1;

    struct s1 {
      int *f0;
      __unsafe_unretained B *f1;
    } f4[2][2];
  } captured_s;

// CHECK-LP64: Block variable layout: BL_UNRETAINED:3, BL_NON_OBJECT_WORD:1, BL_UNRETAINED:1, BL_NON_OBJECT_WORD:1, BL_UNRETAINED:1, BL_NON_OBJECT_WORD:1, BL_UNRETAINED:1, BL_NON_OBJECT_WORD:1, BL_UNRETAINED:1, BL_OPERATOR:0
  void (^c)() = ^{
      id i = captured_s.f0.s_f1;
  };

   c();
}

// Test1 bitfield in cpatured aggregate.
void bf1() {
  struct S {
    int flag : 25;
    int flag1: 7;
    int flag2 :1;
    int flag3: 7;
    int flag4: 24;
  } s;

// CHECK-LP64: Block variable layout: BL_OPERATOR:0
  int (^c)() = ^{
      return s.flag;
  };
  c();
}

// Test2 bitfield in cpatured aggregate.
void bf2() {
  struct S {
    int flag : 1;
  } s;

// CHECK-LP64: Block variable layout: BL_OPERATOR:0
  int (^c)() = ^{
      return s.flag;
  };
  c();
}

// Test3 bitfield in cpatured aggregate.
void bf3() {

     struct {
        unsigned short _reserved : 16;

        unsigned char _draggedNodesAreDeletable: 1;
        unsigned char _draggedOutsideOutlineView : 1;
        unsigned char _adapterRespondsTo_addRootPaths : 1;
        unsigned char _adapterRespondsTo_moveDataNodes : 1;
        unsigned char _adapterRespondsTo_removeRootDataNode : 1;
        unsigned char _adapterRespondsTo_doubleClickDataNode : 1;
        unsigned char _adapterRespondsTo_selectDataNode : 1;
        unsigned char _adapterRespondsTo_textDidEndEditing : 1;
        unsigned char _adapterRespondsTo_updateAndSaveRoots : 1;
        unsigned char _adapterRespondsTo_askToDeleteRootNodes : 1;
        unsigned char _adapterRespondsTo_contextMenuForSelectedNodes : 1;
        unsigned char _adapterRespondsTo_pasteboardFilenamesForNodes : 1;
        unsigned char _adapterRespondsTo_writeItemsToPasteboard : 1;
        unsigned char _adapterRespondsTo_writeItemsToPasteboardXXXX : 1;

        unsigned int _filler : 32;
    } _flags;

// CHECK-LP64: Block variable layout: BL_OPERATOR:0
  unsigned char (^c)() = ^{
      return _flags._draggedNodesAreDeletable;
  };

   c();
}

// Test4 unnamed bitfield
void bf4() {

     struct {
        unsigned short _reserved : 16;

        unsigned char _draggedNodesAreDeletable: 1;
        unsigned char _draggedOutsideOutlineView : 1;
        unsigned char _adapterRespondsTo_addRootPaths : 1;
        unsigned char _adapterRespondsTo_moveDataNodes : 1;
        unsigned char _adapterRespondsTo_removeRootDataNode : 1;
        unsigned char _adapterRespondsTo_doubleClickDataNode : 1;
        unsigned char _adapterRespondsTo_selectDataNode : 1;
        unsigned char _adapterRespondsTo_textDidEndEditing : 1;

        unsigned long long : 64;

        unsigned char _adapterRespondsTo_updateAndSaveRoots : 1;
        unsigned char _adapterRespondsTo_askToDeleteRootNodes : 1;
        unsigned char _adapterRespondsTo_contextMenuForSelectedNodes : 1;
        unsigned char _adapterRespondsTo_pasteboardFilenamesForNodes : 1;
        unsigned char _adapterRespondsTo_writeItemsToPasteboard : 1;
        unsigned char _adapterRespondsTo_writeItemsToPasteboardXXXX : 1;

        unsigned int _filler : 32;
    } _flags;

// CHECK-LP64: Block variable layout: BL_OPERATOR:0
  unsigned char (^c)() = ^{
      return _flags._draggedNodesAreDeletable;
  };

   c();
}



// Test5 unnamed bitfield.
void bf5() {
     struct {
        unsigned char flag : 1;
        unsigned int  : 32;
        unsigned char flag1 : 1;
    } _flags;

// CHECK-LP64: Block variable layout: BL_OPERATOR:0
  unsigned char (^c)() = ^{
      return _flags.flag;
  };

   c();
}


// Test6 0 length bitfield.
void bf6() {
     struct {
        unsigned char flag : 1;
        unsigned int  : 0;
        unsigned char flag1 : 1;
    } _flags;

// CHECK-LP64: Block variable layout: BL_OPERATOR:0
  unsigned char (^c)() = ^{
      return _flags.flag;
  };

   c();
}

// Test7 large number of captured variables.
void Test7() {
    __weak id wid;
    __weak id wid1, wid2, wid3, wid4;
    __weak id wid5, wid6, wid7, wid8;
    __weak id wid9, wid10, wid11, wid12;
    __weak id wid13, wid14, wid15, wid16;
    const id bar = (id) opaque_id();
// CHECK-LP64: Block variable layout: BL_STRONG:1, BL_WEAK:16, BL_OPERATOR:0
    void (^b)() = ^{
      x(bar);
      x(wid1);
      x(wid2);
      x(wid3);
      x(wid4);
      x(wid5);
      x(wid6);
      x(wid7);
      x(wid8);
      x(wid9);
      x(wid10);
      x(wid11);
      x(wid12);
      x(wid13);
      x(wid14);
      x(wid15);
      x(wid16);
    };    
}


// Test 8 very large number of captured variables.
void Test8() {
__weak id wid;
    __weak id wid1, wid2, wid3, wid4;
    __weak id wid5, wid6, wid7, wid8;
    __weak id wid9, wid10, wid11, wid12;
    __weak id wid13, wid14, wid15, wid16;
    __weak id w1, w2, w3, w4;
    __weak id w5, w6, w7, w8;
    __weak id w9, w10, w11, w12;
    __weak id w13, w14, w15, w16;
    const id bar = (id) opaque_id();
// CHECK-LP64: Block variable layout: BL_STRONG:1, BL_WEAK:16, BL_WEAK:16, BL_WEAK:1, BL_OPERATOR:0
    void (^b)() = ^{
      x(bar);
      x(wid1);
      x(wid2);
      x(wid3);
      x(wid4);
      x(wid5);
      x(wid6);
      x(wid7);
      x(wid8);
      x(wid9);
      x(wid10);
      x(wid11);
      x(wid12);
      x(wid13);
      x(wid14);
      x(wid15);
      x(wid16);
      x(w1);
      x(w2);
      x(w3);
      x(w4);
      x(w5);
      x(w6);
      x(w7);
      x(w8);
      x(w9);
      x(w10);
      x(w11);
      x(w12);
      x(w13);
      x(w14);
      x(w15);
      x(w16);
      x(wid);
    };  
}