Bit field-isa pointer

Keywords: iOS simulator Mac github

I. isa pointer structure

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;

#if SUPPORT_PACKED_ISA

    // extra_rc must be the MSB-most field (so it matches carry/overflow flags)
    // nonpointer must be the LSB (fixme or get rid of it)
    // shiftcls must occupy the same bits that a real class pointer would
    // bits + RC_ONE is equivalent to extra_rc + 1
    // RC_HALF is the high bit of extra_rc (i.e. half of its range)

    // future expansion:
    // uintptr_t fast_rr : 1;     // no r/r overrides
    // uintptr_t lock : 2;        // lock for atomic property, @synch
    // uintptr_t extraBytes : 1;  // allocated with extra bytes

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        uintptr_t nonpointer        : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 19;
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    };

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
    struct {
        uintptr_t nonpointer        : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 8;
#       define RC_ONE   (1ULL<<56)
#       define RC_HALF  (1ULL<<7)
    };

# else
#   error unknown architecture for packed isa
# endif

// SUPPORT_PACKED_ISA
#endif


#if SUPPORT_INDEXED_ISA

# if  __ARM_ARCH_7K__ >= 2

#   define ISA_INDEX_IS_NPI      1
#   define ISA_INDEX_MASK        0x0001FFFC
#   define ISA_INDEX_SHIFT       2
#   define ISA_INDEX_BITS        15
#   define ISA_INDEX_COUNT       (1 << ISA_INDEX_BITS)
#   define ISA_INDEX_MAGIC_MASK  0x001E0001
#   define ISA_INDEX_MAGIC_VALUE 0x001C0001
    struct {
        uintptr_t nonpointer        : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t indexcls          : 15;
        uintptr_t magic             : 4;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 7;
#       define RC_ONE   (1ULL<<25)
#       define RC_HALF  (1ULL<<6)
    };

# else
#   error unknown architecture for indexed isa
# endif

// SUPPORT_INDEXED_ISA
#endif

};

Analysis:

1. We know that the isa pointer of the instance object points to the class object of the class to which the object belongs, and the isa of the class object points to its metaclass object.

2. The real machine is based on arm64 architecture, and the simulator and mac computer are based on x86 architecture. The following is illustrated with arm64 as an example.

3. In 64-bit system, the bytes occupied by pointers are 8 or 64 bits.

4. Before arm64, isa was a common pointer that stored the address of class (metaclass) objects; after that, it needed to&

ISA_MASK mask can get the address of the class (metaclass) object. At this time, the isa pointer is a common body, and the stored information is not limited to the address of the class (metaclass) object.

5. Storage information introduction:

 

Among them, shiftcls structural member variables (33 bits) are used to store the addresses of class (metaclass) objects.

 

2. The Address Value Principle of Class (Metaclass) Object-Bit Domain

1. Structures support bit-field operations

// Code

struct bs {
    unsigned a : 9;//If it exceeds the range of bits(511),Then only the values in the range are taken, and the other bits (high bits) are discarded.
    unsigned b : 4;
    unsigned c : 3;
}bit, *pbit;

void test1()
{
    bit.a = 512;//Over-range alarm
    bit.b = 10;
    bit.c = 7;
    NSLog(@"%d,%d,%d\n", bit.a, bit.b, bit.c);
    
    pbit=&bit;
    pbit-> a=0;
    pbit-> b&=3;
    pbit-> c|=1;
    printf("%d,%d,%d\n ",pbit-> a,pbit-> b,pbit-> c);
}

 

 

 

// Output

2019-10-08 18:22:37.051464+0800 SetAndGetsForMask[1966:248996] 0,10,7
0,2,7
 Program ended with exit code: 0

// Analysis

1) unsigned is an unsigned integer, which takes up 4 bytes, and the memory occupied by member variables in the structure is independent and continuous.

2) Take a as an example, the number of digits occupied is 9 bits, that is 0b111111111111 (decimal 511), so the value range of a is 0-511, if it is 512 (binary 0b1000000000), because only 9 bits (000000000000), the value is 0;

3) Bit-by-bit and &: both are 1, otherwise 0; Bit-by-bit or |: both are 0, otherwise 1;

2. Referring to isa, the common body applies the structure, a char character (a byte) stores multiple BOOL values and determines the storage location.

 

2. Set BOOL values for class attributes (setter and getter)

//Person

#import "Person.h"

//mask That is, the mask, which represents the binary number (0). b Beginning)
#define TallMask (1<<0)      //Represents a left shift of 0 bits: b 0000 0001
#define RichMask (1<<1)      //Represents 1 left shift 1 bit:0 b 0000 0010
#define HandsomeMask (1<<2)  //Represents 1 left shift 2 bits:0 b 0000 0100

//Expansion: 10<<3 That is to add three zeros after the corresponding binary number of 10.

@interface Person()
{
    char _saveBox;
}

@end

@implementation Person

- (instancetype)init
{
    if (self = [super init]) {
        //Store three variables in one byte: from the right to the left Tall,Rich,Handsome
        _saveBox = 0b00000101;
    }
    return self;
}

/*thinking
 0000 0101(_saveBox)
|0000 0001(Mask)
 ---------
 0000 0001(Assignment tall is 1)
 
 0000 0101
&1111 1110(Mask inversion)
 ---------
 0000 0100(Assignment tall is 0)
 
 1.If the assigned value is 1, then bitwise or;
 2.If the assigned value is 0, the mask is reversed first, followed by bitwise and.
 */
- (void)setTall:(BOOL)tall
{
    if (tall) {
        _saveBox |= TallMask;
    } else {
        _saveBox &= ~TallMask;
    }
}

- (void)setRich:(BOOL)rich
{
    if (rich) {
        _saveBox |= RichMask;
    } else {
        _saveBox &= ~RichMask;
    }
}

- (void)setHandsome:(BOOL)handsome
{
    if (handsome) {
        _saveBox |= HandsomeMask;
    } else {
        _saveBox &= ~HandsomeMask;
    }
}

/*thinking
 0000 0101
&0000 0001
 ---------
 0000 0001(Take out the tall value)
 
 1.Bit-by-bit and mask out the specific bit in _saveBox;
 2.Result >= 1, take the inverse 0, then take the inverse 1; Similarly, take the inverse 0, then take the inverse 0.
 */
- (BOOL)isTall
{
    return !!(_saveBox & TallMask);
}

- (BOOL)isRich
{
    return !!(_saveBox & RichMask);
}

- (BOOL)isHandsome
{
    return !!(_saveBox & HandsomeMask);
}

@end

 

//Student

#import "Student.h"

@interface Student()
{
    /*thinking
     1.Using a structure to store variables;
     2.Structures support bit fields: one char character per byte (0b0000 0000) in sequence, tall, rich, handsome from the rightmost to the left;
     */
    struct {
        char tall : 1;//Store in one bit
        char rich : 1;
        char handsome : 1;
    }_tallRichHandsome;
}

@end

@implementation Student

- (void)setTall:(BOOL)tall
{
    _tallRichHandsome.tall = tall;
}

- (void)setRich:(BOOL)rich
{
    _tallRichHandsome.rich = rich;
}

- (void)setHandsome:(BOOL)handsome
{
    _tallRichHandsome.handsome = handsome;
}

- (BOOL)isTall
{
    return !!_tallRichHandsome.tall;//The inverse of non-zero (including negative numbers) is zero
}

- (BOOL)isRich
{
    return !!_tallRichHandsome.rich;
}

- (BOOL)isHandsome
{
    return !!_tallRichHandsome.handsome;
}

@end

 

//Worker

#import "Worker.h"

#define TallMask (1<<0)//You can also move 6 bits to the left, the remaining bits are not used.
#define RichMask (1<<1)
#define HandsomeMask (1<<2)
#define ThinMask (1<<3)

@interface  Worker()
{
    //Design Ideas of Apple System
    union {
        char bits;//All member variables in a byte storage structure
        struct {//Arrangement: Bit Domain, Increase Readability
            char tall : 1;//Occupy one place
            char rich : 1;
            char handsome : 1;
            char thin : 1;
        };
    }_tallRichHandsome;
}

@end

@implementation Worker

- (void)setTall:(BOOL)tall
{
    if (tall) {
        NSLog(@"----%c", _tallRichHandsome.bits);
        _tallRichHandsome.bits |= TallMask;
    } else {
        _tallRichHandsome.bits &= ~TallMask;
    }
}

- (void)setRich:(BOOL)rich
{
    if (rich) {
        _tallRichHandsome.bits |= RichMask;
    } else {
        _tallRichHandsome.bits &= ~RichMask;
    }
}

- (void)setHandsome:(BOOL)handsome
{
    if (handsome) {
        _tallRichHandsome.bits |= HandsomeMask;
    } else {
        _tallRichHandsome.bits &= ~HandsomeMask;
    }
}

- (void)setThin:(BOOL)thin
{
    if (thin) {
        _tallRichHandsome.bits |= ThinMask;
    } else {
        _tallRichHandsome.bits &= ~ThinMask;
    }
}

- (BOOL)isTall
{
    return !!(_tallRichHandsome.bits & TallMask);
}

- (BOOL)isRich
{
    return !!(_tallRichHandsome.bits & RichMask);
}

- (BOOL)isHandsome
{
    return !!(_tallRichHandsome.bits & HandsomeMask);
}

- (BOOL)isThin
{
    return !!(_tallRichHandsome.bits & ThinMask);
}

@end

 

//main

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
#import "Worker.h"
#import "Engineer.h"

struct bs {
    unsigned a : 9;//If it exceeds the range of bits(511),Then only the values in the range are taken, and the other bits (high bits) are discarded.
    unsigned b : 4;
    unsigned c : 3;
}bit, *pbit;

void test1()
{
    bit.a = 512;//Over-range alarm
    bit.b = 10;
    bit.c = 7;
    NSLog(@"%d,%d,%d\n", bit.a, bit.b, bit.c);
    
    pbit=&bit;
    pbit-> a=0;
    pbit-> b&=3;
    pbit-> c|=1;
    printf("%d,%d,%d\n ",pbit-> a,pbit-> b,pbit-> c);
}

void test2()
{
    Person *per = [[Person alloc] init];
    per.tall = NO;
    per.rich = NO;
    per.handsome = YES;
    NSLog(@"%d %d %d", per.isTall, per.isRich, per.isHandsome);
}

void test3()
{
    Student *stu = [[Student alloc] init];
    stu.tall = YES;
    stu.rich = NO;
    stu.handsome = YES;
    NSLog(@"%d %d %d", stu.isTall, stu.isRich, stu.isHandsome);
}

void test4()
{
    Worker *worker = [[Worker alloc] init];
//    worker.tall = YES;
    worker.rich = NO;
    worker.handsome = NO;
    worker.thin = YES;
    NSLog(@"%d %d %d", worker.isThin, worker.isRich, worker.isHandsome);
}

void test5()
{
    Engineer *engineer = [[Engineer alloc] init];
//    engineer.age = 12;
//    engineer.level = 6;
//    engineer.workers = 5;
    
    //0b 1111 1111 1111 1111(Decimal: 65535)
    //0b 0010 1100 1110 1101(Decimal: 11501)
    engineer->_personalInfo.bits =11501;
    NSLog(@"%d %d %d", engineer.getAge, engineer.getLevel, engineer.getWorkers);
    //2019-10-08 16:42:09.612140+0800 SetAndGetsForMask[1488:127227] 7 16 8160
    //
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        test1();
//        test2();
//        test3();
//        test4();
//        test5();
    }
    return 0;
}

 

// Printing

2019-10-09 10:42:04.998750+0800 SetAndGetsForMask[2513:316066] 0 0 1
2019-10-09 10:42:04.999093+0800 SetAndGetsForMask[2513:316066] 1 0 1
2019-10-09 10:42:04.999122+0800 SetAndGetsForMask[2513:316066] 1 0 0
Program ended with exit code: 0

// Analysis (Take Worker as an example)

1) All members in the common body occupy a memory area, the size of which is equal to the maximum number of bytes occupied by that member.

2) The compiler does not compute the memory of the structure in Worker and defines variables, but only increases readability.

3) There is only one char variable bits (one byte) in Worker, so the common volume variable _tallRichHandsome also takes one byte.

4) The location domain of the structure limits the range of values of the variable (one bit: 0 or 1), and the mask specifies the location (on which) the variable is stored.

 

3. Setting class attributes of non-BOOL types (setter and getter) - Limiting the range of variable values and specifying storage locations

//Engineer

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

//Bit field location (variable value storage location)
#define AgeMask 0b00000111//Minimum three-digit storage
#define LevelMask (1<<4)//Low-to-high-digit, 5th-digit storage
#define WorkersMask 0b0001111111100000

@interface Engineer : NSObject
{
    @public
    union {
        int bits;
        struct {//Location Range (Variable Value Range)
            int age : 3;
            int level : 1;
            int workers : 8;
        };
    }_personalInfo;
}

//- (void)setAge:(int)age;
//- (void)setLevel:(int)level;
//- (void)setWorkers:(int)workers;

- (int)getAge;
- (int)getLevel;
- (int)getWorkers;

@end

NS_ASSUME_NONNULL_END



#import "Engineer.h"

@implementation Engineer

//- (void)setAge:(int)age
//{
//    self->_personalInfo.bits |= AgeMask;
//}
//
//- (void)setLevel:(int)level
//{
//    self->_personalInfo.bits |= LevelMask;
//}
//
//- (void)setWorkers:(int)workers
//{
//    self->_personalInfo.bits |= WorkersMask;
//}

- (int)getAge
{
    return self->_personalInfo.bits & AgeMask;
}

- (int)getLevel
{
     return self->_personalInfo.bits & LevelMask;
}

- (int)getWorkers
{
     return self->_personalInfo.bits & WorkersMask;
}

@end

// Printing

2019-10-09 11:08:14.617655+0800 SetAndGetsForMask[2630:349068] 5 0 3296
Program ended with exit code: 0

// Description

1) mask masks can be represented either directly by binary (0b beginning) or hexadecimal (0x beginning) or by left-shift symbols (generally used in the case of bit field 1).

2) Mask means occupied digits: 1 means occupied digits, 0 is not occupied; and occupied digits should be continuous, there is no case of 1 on both sides and 0 in the middle;

 

III. CONCLUSION

1. After arm64, isa is a pointer of common body type, which stores all member variables in the internal applied structure.

2. Restrict the value range of member variables according to the structure's bit field, and use masks to specify the storage location of member variables, and extract the value of member variables at a specific location by bit and operation of masks.

For example, after bits are used to locate and calculate ISA_MASK, the address of the class (metaclass) object is obtained.

 

It can be seen that the bit field of shiftcls member variable is 33 bits, and the storage position of bits variable is as follows: from the fourth highest position, the lowest three are empty.

———— Therefore, in the arm64 architecture, the lowest three bits of all class and metaclass object addresses in binary representation are 0, and the lowest one in hexadecimal representation is 0 or 8 (this print address with class and object_getClass is not shown here)!

 

GitHub

Posted by robinhood on Wed, 09 Oct 2019 15:36:21 -0700