LLVM: lib/Support/OptimizedStructLayout.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

14#include

15

16using namespace llvm;

17

19

20#ifndef NDEBUG

24 Align ComputedMaxAlign;

25 for (auto &Field : Fields) {

27 "didn't assign a fixed offset to field");

29 "didn't assign a correctly-aligned offset to field");

31 "didn't assign offsets in ascending order");

34 "didn't compute MaxAlign correctly");

35 ComputedMaxAlign = std::max(Field.Alignment, MaxAlign);

36 }

37 assert(LastEnd == Size && "didn't compute LastEnd correctly");

38 assert(ComputedMaxAlign == MaxAlign && "didn't compute MaxAlign correctly");

39}

40#endif

41

42std::pair<uint64_t, Align>

44#ifndef NDEBUG

45

46 {

47 bool InFixedPrefix = true;

48 size_t LastEnd = 0;

49 for (auto &Field : Fields) {

52 assert(InFixedPrefix &&

53 "fixed-offset fields are not a strict prefix of array");

55 "fixed-offset fields overlap or are not in order");

58 "overflow in fixed-offset end offset");

59 } else {

60 InFixedPrefix = false;

61 }

62 }

63 }

64#endif

65

66

68

69

70 auto FirstFlexible = Fields.begin(), E = Fields.end();

71 while (FirstFlexible != E && FirstFlexible->hasFixedOffset()) {

72 MaxAlign = std::max(MaxAlign, FirstFlexible->Alignment);

73 ++FirstFlexible;

74 }

75

76

77 if (FirstFlexible == E) {

79 if (!Fields.empty())

80 Size = Fields.back().getEndOffset();

81

82#ifndef NDEBUG

84#endif

85 return std::make_pair(Size, MaxAlign);

86 }

87

88

89

90

91

92

93 {

94 uintptr_t UniqueNumber = 0;

95 for (auto I = FirstFlexible; I != E; ++I) {

96 I->Scratch = reinterpret_cast<void*>(UniqueNumber++);

97 MaxAlign = std::max(MaxAlign, I->Alignment);

98 }

99 }

100

101

102

103

104

105

107 [](const Field *lhs, const Field *rhs) -> int {

108

111

112

114 return (lhs->Size < rhs->Size ? 1 : -1);

115

116

117 auto lhsNumber = reinterpret_cast<uintptr_t>(lhs->Scratch);

118 auto rhsNumber = reinterpret_cast<uintptr_t>(rhs->Scratch);

119 if (lhsNumber != rhsNumber)

120 return (lhsNumber < rhsNumber ? -1 : 1);

121

122 return 0;

123 });

124

125

126

127

128

129

130

131 {

132 bool HasPadding = false;

134

135

136 for (auto I = Fields.begin(); I != FirstFlexible; ++I) {

137 assert(I->hasFixedOffset());

138 if (LastEnd != I->Offset) {

139 HasPadding = true;

140 break;

141 }

142 LastEnd = I->getEndOffset();

143 }

144

145

146

147

148

149

150 if (!HasPadding) {

151 for (auto I = FirstFlexible; I != E; ++I) {

153 if (LastEnd != Offset) {

154 HasPadding = true;

155 break;

156 }

158 LastEnd = I->getEndOffset();

159 }

160 }

161

162

163 if (!HasPadding) {

164#ifndef NDEBUG

166#endif

167 return std::make_pair(LastEnd, MaxAlign);

168 }

169 }

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236 struct AlignmentQueue {

237

239

240

241

242

243

244

246

247

249

252 }

253 };

255 for (auto I = FirstFlexible; I != E; ) {

256 auto Head = I;

257 auto Alignment = I->Alignment;

258

260 auto LastInQueue = I;

261 for (++I; I != E && I->Alignment == Alignment; ++I) {

262 LastInQueue->Scratch = I;

263 LastInQueue = I;

264 MinSize = std::min(MinSize, I->Size);

265 }

266 LastInQueue->Scratch = nullptr;

267

268 FlexibleFieldsByAlignment.push_back({MinSize, Head, Alignment});

269 }

270

271#ifndef NDEBUG

272

273 auto checkQueues = [&]{

274 bool FirstQueue = true;

275 Align LastQueueAlignment;

276 for (auto &Queue : FlexibleFieldsByAlignment) {

277 assert((FirstQueue || Queue.Alignment < LastQueueAlignment) &&

278 "bins not in order of descending alignment");

279 LastQueueAlignment = Queue.Alignment;

280 FirstQueue = false;

281

282 assert(Queue.Head && "queue was empty");

284 for (auto I = Queue.Head; I; I = Queue.getNext(I)) {

285 assert(I->Alignment == Queue.Alignment && "bad field in queue");

286 assert(I->Size <= LastSize && "queue not in descending size order");

287 LastSize = I->Size;

288 }

289 }

290 };

291 checkQueues();

292#endif

293

294

295 auto spliceFromQueue = [&](AlignmentQueue *Queue, Field *Last, Field *Cur) {

296 assert(Last ? Queue->getNext(Last) == Cur : Queue->Head == Cur);

297

298

299

301 Last->Scratch = Cur->Scratch;

302

303

304

305

306 if (!Cur->Scratch)

307 Queue->MinSize = Last->Size;

308

309

310 } else {

311 if (auto NewHead = Queue->getNext(Cur))

312 Queue->Head = NewHead;

313

314

315 else

316 FlexibleFieldsByAlignment.erase(Queue);

317 }

318 };

319

320

321

324

325

327

328

329

330 auto addToLayout = [&](AlignmentQueue *Queue, Field *Last, Field *Cur,

333

334

335 spliceFromQueue(Queue, Last, Cur);

336

337

340 LastEnd = Layout.back().getEndOffset();

341

342

343 return true;

344 };

345

346

347

348

349 auto tryAddFillerFromQueue = [&](AlignmentQueue *Queue, uint64_t StartOffset,

350 std::optional<uint64_t> EndOffset) -> bool {

352 assert(StartOffset == alignTo(LastEnd, Queue->Alignment));

353 assert(!EndOffset || StartOffset < *EndOffset);

354

355

356

357 auto MaxViableSize =

358 (EndOffset ? *EndOffset - StartOffset : ~(uint64_t)0);

359 if (Queue->MinSize > MaxViableSize)

360 return false;

361

362

363

364 for (Field *Cur = Queue->Head, *Last = nullptr; true;

365 Last = Cur, Cur = Queue->getNext(Cur)) {

366 assert(Cur && "didn't find a match in queue despite its MinSize");

367 if (Cur->Size <= MaxViableSize)

368 return addToLayout(Queue, Last, Cur, StartOffset);

369 }

370

371 llvm_unreachable("didn't find a match in queue despite its MinSize");

372 };

373

374

375

376 auto tryAddBestField = [&](std::optional<uint64_t> BeforeOffset) -> bool {

377 assert(!BeforeOffset || LastEnd < *BeforeOffset);

378 auto QueueB = FlexibleFieldsByAlignment.begin();

379 auto QueueE = FlexibleFieldsByAlignment.end();

380

381

382

383 auto FirstQueueToSearch = QueueB;

384 for (; FirstQueueToSearch != QueueE; ++FirstQueueToSearch) {

385 if (isAligned(FirstQueueToSearch->Alignment, LastEnd))

386 break;

387 }

388

390 while (true) {

391

392

393

394

395

396 for (auto Queue = FirstQueueToSearch; Queue != QueueE; ++Queue) {

397 if (tryAddFillerFromQueue(Queue, Offset, BeforeOffset))

398 return true;

399 }

400

401

402 QueueE = FirstQueueToSearch;

403

404

405 if (FirstQueueToSearch == QueueB)

406 return false;

407

408

409

410

411 --FirstQueueToSearch;

412 Offset = alignTo(LastEnd, FirstQueueToSearch->Alignment);

413 if (BeforeOffset && Offset >= *BeforeOffset)

414 return false;

415 while (FirstQueueToSearch != QueueB &&

416 Offset == alignTo(LastEnd, FirstQueueToSearch[-1].Alignment))

417 --FirstQueueToSearch;

418 }

419 };

420

421

422

423 for (auto I = Fields.begin(); I != FirstFlexible; ++I) {

425 while (LastEnd != I->Offset) {

426 if (!tryAddBestField(I->Offset))

427 break;

428 }

430 LastEnd = I->getEndOffset();

431 }

432

433#ifndef NDEBUG

434 checkQueues();

435#endif

436

437

438

439 while (!FlexibleFieldsByAlignment.empty()) {

440 bool Success = tryAddBestField(std::nullopt);

441 assert(Success && "didn't find a field with no fixed limit?");

443 }

444

445

447 memcpy(Fields.data(), Layout.data(),

449

450#ifndef NDEBUG

451

453#endif

454

455 return std::make_pair(LastEnd, MaxAlign);

456}

static void checkValidLayout(ArrayRef< Field > Fields, uint64_t Size, Align MaxAlign)

This file provides an interface for laying out a sequence of fields as a struct in a way that attempt...

assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())

ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...

size_t size() const

size - Get the array size.

bool empty() const

empty - Check if the array is empty.

MutableArrayRef - Represent a mutable reference to an array (0 or more elements consecutively in memo...

T & back() const

back - Get the last element.

void reserve(size_type N)

iterator erase(const_iterator CI)

void push_back(const T &Elt)

pointer data()

Return a pointer to the vector's buffer, even if empty().

This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.

#define llvm_unreachable(msg)

Marks that the current location is not supposed to be reachable.

This is an optimization pass for GlobalISel generic memory operations.

bool isAligned(Align Lhs, uint64_t SizeInBytes)

Checks that SizeInBytes is a multiple of the alignment.

std::pair< uint64_t, Align > performOptimizedStructLayout(MutableArrayRef< OptimizedStructLayoutField > Fields)

Compute a layout for a struct containing the given fields, making a best-effort attempt to minimize t...

uint64_t alignTo(uint64_t Size, Align A)

Returns a multiple of A needed to store Size bytes.

void array_pod_sort(IteratorTy Start, IteratorTy End)

array_pod_sort - This sorts an array with the specified start and end extent.

This struct is a compact representation of a valid (non-zero power of two) alignment.

Align Alignment

The required alignment of this field.

uint64_t getEndOffset() const

Given that this field has a fixed offset, return the offset of the first byte following it.

void * Scratch

Private scratch space for the algorithm.

bool hasFixedOffset() const

Return true if this field has been assigned a fixed offset.

uint64_t Offset

The offset of this field in the final layout.

uint64_t Size

The required size of this field in bytes.