From: Justin Bogner Date: Thu, 14 Aug 2014 02:42:10 +0000 (+0000) Subject: CodeGen: When bitfields fall on natural boundaries, split them up X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=12d879120bde0fd529b5c87e5102841a0770ae7b;p=clang CodeGen: When bitfields fall on natural boundaries, split them up Currently when laying out bitfields that don't need any padding, we represent them as a wide enough int to contain all of the bits. This can be hard on the backend since we'll do things like represent stores to a few bits as loading an i144, masking it with a large constant, and storing it back. This turns up in less pathological cases where we load and mask 64 bit word on a 32 bit platform when we actually only need to access 32 bits. This leads to bad code being generated in most of our 32 bit backends. In practice, there are often natural breaks in bitfields, and it's a fairly simple and effective heuristic to split these fields into legal integer sized chunks when it will be equivalent (ie, it won't force us to add any extra padding). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@215614 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGRecordLayoutBuilder.cpp b/lib/CodeGen/CGRecordLayoutBuilder.cpp index a10d8e791b..06192bd2ee 100644 --- a/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -377,6 +377,10 @@ CGRecordLowering::accumulateBitFields(RecordDecl::field_iterator Field, } return; } + + llvm::Type *WordType = + DataLayout.getLargestLegalIntType(Types.getLLVMContext()); + uint64_t WordSize = WordType ? DataLayout.getTypeSizeInBits(WordType) : 0; for (;;) { // Check to see if we need to start a new run. if (Run == FieldEnd) { @@ -392,9 +396,12 @@ CGRecordLowering::accumulateBitFields(RecordDecl::field_iterator Field, ++Field; continue; } - // Add bitfields to the run as long as they qualify. + // Add bitfields to the run as long as they qualify. If we end up on a word + // boundary we insert a break since it's equivalent and very wide types are + // harder to optimize with. if (Field != FieldEnd && Field->getBitWidthValue(Context) != 0 && - Tail == getFieldBitOffset(*Field)) { + Tail == getFieldBitOffset(*Field) && + WordSize != Tail - StartBitOffset) { Tail += Field->getBitWidthValue(Context); ++Field; continue; diff --git a/test/CodeGen/bitfield-machinewords.c b/test/CodeGen/bitfield-machinewords.c new file mode 100644 index 0000000000..3d370e7112 --- /dev/null +++ b/test/CodeGen/bitfield-machinewords.c @@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s -check-prefix=CHECK32 +// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -emit-llvm -o - | FileCheck %s -check-prefix=CHECK64 + +typedef unsigned long long uint64_t; + +struct thirty_two_bit_fields { + unsigned int ttbf1 : 32; + unsigned int ttbf2 : 32; + unsigned int ttbf3 : 32; + unsigned int ttbf4 : 32; +}; +void ttbf(struct thirty_two_bit_fields *x) {} +// CHECK32: %struct.thirty_two_bit_fields = type { i32, i32, i32, i32 } +// CHECK64: %struct.thirty_two_bit_fields = type { i64, i64 } + +struct thirty_two_in_sixty_four { + uint64_t ttisf1 : 32; + uint64_t ttisf2 : 32; + uint64_t ttisf3 : 32; + uint64_t ttisf4 : 32; +}; +void ttisf(struct thirty_two_in_sixty_four *x) {} +// CHECK32: %struct.thirty_two_in_sixty_four = type { i32, i32, i32, i32 } +// CHECK64: %struct.thirty_two_in_sixty_four = type { i64, i64 } + +struct everything_fits { + unsigned int ef1 : 2; + unsigned int ef2 : 29; + unsigned int ef3 : 1; + + unsigned int ef4 : 16; + unsigned int ef5 : 16; + + unsigned int ef6 : 7; + unsigned int ef7 : 25; +}; +void ef(struct everything_fits *x) {} +// CHECK32: %struct.everything_fits = type { i32, i32, i32 } +// CHECK64: %struct.everything_fits = type <{ i64, i32 }> + +struct not_lined_up { + uint64_t nlu1 : 31; + uint64_t nlu2 : 2; + uint64_t nlu3 : 32; + uint64_t nlu4 : 31; +}; +void nlu(struct not_lined_up *x) {} +// CHECK32: %struct.not_lined_up = type { i96 } +// CHECK64: %struct.not_lined_up = type { i40, i64 } + +struct padding_between_words { + unsigned int pbw1 : 16; + unsigned int pbw2 : 14; + + unsigned int pbw3 : 12; + unsigned int pbw4 : 16; + + unsigned int pbw5 : 8; + unsigned int pbw6 : 10; + + unsigned int pbw7 : 20; + unsigned int pbw8 : 10; +}; +void pbw(struct padding_between_words *x) {} +// CHECK32: %struct.padding_between_words = type { i32, i32, i24, i32 } +// CHECK64: %struct.padding_between_words = type { i32, i32, i24, i32 } + +struct unaligned_are_coalesced { + uint64_t uac1 : 16; + uint64_t uac2 : 32; + uint64_t uac3 : 16; + uint64_t uac4 : 48; + uint64_t uac5 : 64; + uint64_t uac6 : 16; + uint64_t uac7 : 32; +}; +void uac(struct unaligned_are_coalesced *x) {} +// CHECK32: %struct.unaligned_are_coalesced = type { i112, i112 } +// CHECK64: %struct.unaligned_are_coalesced = type { i64, i48, i64, i48 }