Skip to content

Commit f966020

Browse files
authored
Merge pull request #23884 from linux-on-ibm-z/s390x-gentype-fix
IRGen: fix get/store enum tag functions for big-endian platforms
2 parents 1743235 + 04976e1 commit f966020

File tree

1 file changed

+145
-100
lines changed

1 file changed

+145
-100
lines changed

lib/IRGen/GenType.cpp

Lines changed: 145 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,110 @@ static llvm::Value *computeExtraTagBytes(IRGenFunction &IGF, IRBuilder &Builder,
421421
return phi;
422422
}
423423

424+
/// Emit a specialized memory operation for a \p size of 0, 1, 2 or
425+
/// 4 bytes.
426+
/// \p emitMemOpFn will be called in a basic block where \p size is
427+
/// a known constant value passed as a parameter to the function.
428+
static void emitSpecializedMemOperation(
429+
IRGenFunction &IGF,
430+
llvm::function_ref<void(IRBuilder &, Size)> emitMemOpFn,
431+
llvm::Value *size) {
432+
auto &Ctx = IGF.IGM.getLLVMContext();
433+
auto &Builder = IGF.Builder;
434+
435+
// Sizes to try. Tested in order.
436+
const auto sizes = std::array<Size, 4>{
437+
Size(0),
438+
Size(1),
439+
Size(2),
440+
Size(4),
441+
};
442+
443+
// Block to jump to after successful match.
444+
auto *returnBB = llvm::BasicBlock::Create(Ctx);
445+
446+
// Test all the sizes in turn, generating code similar to:
447+
//
448+
// if (size == 0) {
449+
// <op>
450+
// } else if (size == 1) {
451+
// <op>
452+
// } else if (size == 2) {
453+
// <op>
454+
// } else if (size == 4) {
455+
// <op>
456+
// } else {
457+
// <unreachable>
458+
// }
459+
//
460+
for (const Size &s : sizes) {
461+
auto *matchBB = llvm::BasicBlock::Create(Ctx);
462+
auto *nextBB = llvm::BasicBlock::Create(Ctx);
463+
464+
// Check if size matches.
465+
auto *imm = Builder.getInt32(s.getValue());
466+
auto *cmp = Builder.CreateICmpEQ(size, imm);
467+
Builder.CreateCondBr(cmp, matchBB, nextBB);
468+
469+
// Size matches: execute sized operation.
470+
Builder.emitBlock(matchBB);
471+
emitMemOpFn(Builder, s);
472+
Builder.CreateBr(returnBB);
473+
474+
// Size does not match: try next size.
475+
Builder.emitBlock(nextBB);
476+
}
477+
// No size matched. Should never happen.
478+
Builder.CreateUnreachable();
479+
480+
// Continue.
481+
Builder.emitBlock(returnBB);
482+
}
483+
484+
/// Emit a load of a \p size byte integer value zero extended to 4 bytes.
485+
/// \p size may be 0, 1, 2 or 4.
486+
static llvm::Value *emitGetTag(IRGenFunction &IGF,
487+
Address from,
488+
llvm::Value *size) {
489+
auto *phi = llvm::PHINode::Create(IGF.IGM.Int32Ty, 4);
490+
emitSpecializedMemOperation(IGF,
491+
[=](IRBuilder &B, Size s) {
492+
if (s == Size(0)) {
493+
// If the size is 0 bytes return 0.
494+
phi->addIncoming(B.getInt32(0), B.GetInsertBlock());
495+
return;
496+
}
497+
// Generate a load of size bytes and zero-extend it to 32-bits.
498+
auto *type = B.getIntNTy(s.getValueInBits());
499+
Address addr = B.CreateBitCast(from, type->getPointerTo());
500+
auto *val = B.CreateZExtOrTrunc(B.CreateLoad(addr), B.getInt32Ty());
501+
phi->addIncoming(val, B.GetInsertBlock());
502+
},
503+
size);
504+
IGF.Builder.Insert(phi);
505+
return phi;
506+
}
507+
508+
/// Emit a store of \p val truncated to \p size bytes.
509+
/// \p size may be 0, 1, 2 or 4.
510+
static void emitSetTag(IRGenFunction &IGF,
511+
Address to, llvm::Value *val,
512+
llvm::Value *size) {
513+
emitSpecializedMemOperation(IGF,
514+
[=](IRBuilder &B, Size s) {
515+
if (s == Size(0)) {
516+
// Nothing to store.
517+
return;
518+
}
519+
// Store value truncated to size bytes.
520+
auto *type = B.getIntNTy(s.getValueInBits());
521+
auto *trunc = B.CreateZExtOrTrunc(val, type);
522+
Address addr = B.CreateBitCast(to, type->getPointerTo());
523+
B.CreateStore(trunc, addr);
524+
},
525+
size);
526+
}
527+
424528
llvm::Value *FixedTypeInfo::getEnumTagSinglePayload(IRGenFunction &IGF,
425529
llvm::Value *numEmptyCases,
426530
Address enumAddr,
@@ -467,26 +571,21 @@ llvm::Value *irgen::getFixedTypeEnumTagSinglePayload(IRGenFunction &IGF,
467571

468572
// There are extra tag bits to check.
469573
Builder.emitBlock(extraTagBitsBB);
470-
Address extraTagBitsSlot = IGF.createAlloca(IGM.Int32Ty, Alignment(4));
471-
Builder.CreateStore(zero, extraTagBitsSlot);
472574

473575
// Compute the number of extra tag bytes.
474576
auto *emptyCases = Builder.CreateSub(numEmptyCases, numExtraInhabitants);
475577
auto *numExtraTagBytes =
476578
computeExtraTagBytes(IGF, Builder, fixedSize, emptyCases);
477579

478-
// Read the extra tag bytes.
580+
// Read the value stored in the extra tag bytes.
479581
auto *valueAddr =
480582
Builder.CreateBitOrPointerCast(enumAddr.getAddress(), IGM.Int8PtrTy);
481583
auto *extraTagBitsAddr =
482584
Builder.CreateConstInBoundsGEP1_32(IGM.Int8Ty, valueAddr,
483-
fixedSize.getValue());
484-
485-
// TODO: big endian.
486-
Builder.CreateMemCpy(
487-
Builder.CreateBitCast(extraTagBitsSlot, IGM.Int8PtrTy).getAddress(), 1,
488-
extraTagBitsAddr, 1, numExtraTagBytes);
489-
auto extraTagBits = Builder.CreateLoad(extraTagBitsSlot);
585+
fixedSize.getValue());
586+
auto *extraTagBits = emitGetTag(IGF,
587+
Address(extraTagBitsAddr, Alignment(1)),
588+
numExtraTagBytes);
490589

491590
extraTagBitsBB = llvm::BasicBlock::Create(Ctx);
492591
Builder.CreateCondBr(Builder.CreateICmpEQ(extraTagBits, zero),
@@ -497,20 +596,25 @@ llvm::Value *irgen::getFixedTypeEnumTagSinglePayload(IRGenFunction &IGF,
497596
Builder.emitBlock(extraTagBitsBB);
498597

499598
auto *truncSize = Builder.CreateZExtOrTrunc(size, IGM.Int32Ty);
500-
Address caseIndexFromValueSlot = IGF.createAlloca(IGM.Int32Ty, Alignment(4));
501-
Builder.CreateStore(zero, caseIndexFromValueSlot);
502599

503600
auto *caseIndexFromExtraTagBits = Builder.CreateSelect(
504601
Builder.CreateICmpUGE(truncSize, four), zero,
505602
Builder.CreateShl(Builder.CreateSub(extraTagBits, one),
506603
Builder.CreateMul(eight, truncSize)));
507604

508-
// TODO: big endian.
509-
Builder.CreateMemCpy(
510-
Builder.CreateBitCast(caseIndexFromValueSlot, IGM.Int8PtrTy),
511-
Address(valueAddr, Alignment(1)),
512-
std::min(Size(4U), fixedSize));
513-
auto caseIndexFromValue = Builder.CreateLoad(caseIndexFromValueSlot);
605+
llvm::Value *caseIndexFromValue = zero;
606+
if (fixedSize > Size(0)) {
607+
// Read up to one pointer-sized 'chunk' of the payload.
608+
// The size of the chunk does not have to be a power of 2.
609+
Size limit = IGM.getPointerSize();
610+
auto *caseIndexType = llvm::IntegerType::get(Ctx,
611+
std::min(limit, fixedSize).getValueInBits());
612+
auto *caseIndexAddr = Builder.CreateBitCast(valueAddr,
613+
caseIndexType->getPointerTo());
614+
caseIndexFromValue = Builder.CreateZExtOrTrunc(
615+
Builder.CreateLoad(Address(caseIndexAddr, Alignment(1))),
616+
IGM.Int32Ty);
617+
}
514618

515619
auto *result1 = Builder.CreateAdd(
516620
numExtraInhabitants,
@@ -544,72 +648,6 @@ llvm::Value *irgen::getFixedTypeEnumTagSinglePayload(IRGenFunction &IGF,
544648
return Builder.CreateAdd(result, llvm::ConstantInt::get(IGM.Int32Ty, 1));
545649
}
546650

547-
/// Emit a speciaize memory operation for a \p size of 0 to 4 bytes.
548-
static void emitSpecializedMemOperation(
549-
IRGenFunction &IGF,
550-
llvm::function_ref<void(IRBuilder &, Size)> emitMemOpFn,
551-
llvm::Value *size) {
552-
auto &IGM = IGF.IGM;
553-
auto &Ctx = IGF.IGM.getLLVMContext();
554-
auto &Builder = IGF.Builder;
555-
auto *returnBB = llvm::BasicBlock::Create(Ctx);
556-
auto *oneBB = llvm::BasicBlock::Create(Ctx);
557-
auto *twoBB = llvm::BasicBlock::Create(Ctx);
558-
auto *fourBB = llvm::BasicBlock::Create(Ctx);
559-
auto *int32Ty = IGM.Int32Ty;
560-
auto *zero = llvm::ConstantInt::get(int32Ty, 0U);
561-
auto *one = llvm::ConstantInt::get(int32Ty, 1U);
562-
auto *two = llvm::ConstantInt::get(int32Ty, 2U);
563-
564-
auto *continueBB = llvm::BasicBlock::Create(Ctx);
565-
auto *isZero = Builder.CreateICmpEQ(size, zero);
566-
Builder.CreateCondBr(isZero, returnBB, continueBB);
567-
568-
Builder.emitBlock(continueBB);
569-
continueBB = llvm::BasicBlock::Create(Ctx);
570-
auto *isOne = Builder.CreateICmpEQ(size, one);
571-
Builder.CreateCondBr(isOne, oneBB, continueBB);
572-
573-
Builder.emitBlock(continueBB);
574-
auto *isTwo = Builder.CreateICmpEQ(size, two);
575-
Builder.CreateCondBr(isTwo, twoBB, fourBB);
576-
577-
Builder.emitBlock(oneBB);
578-
emitMemOpFn(Builder, Size(1));
579-
Builder.CreateBr(returnBB);
580-
581-
Builder.emitBlock(twoBB);
582-
emitMemOpFn(Builder, Size(2));
583-
Builder.CreateBr(returnBB);
584-
585-
Builder.emitBlock(fourBB);
586-
emitMemOpFn(Builder, Size(4));
587-
Builder.CreateBr(returnBB);
588-
589-
Builder.emitBlock(returnBB);
590-
}
591-
592-
/// Emit a memset of zero operation for a \p size of 0 to 4 bytes.
593-
static void emitMemZero(IRGenFunction &IGF, Address addr,
594-
llvm::Value *size) {
595-
auto *zeroByte = llvm::ConstantInt::get(IGF.IGM.Int8Ty, 0U);
596-
emitSpecializedMemOperation(IGF,
597-
[=](IRBuilder &B, Size numBytes) {
598-
B.CreateMemSet(addr, zeroByte, numBytes);
599-
},
600-
size);
601-
}
602-
603-
/// Emit a memcpy operation for a \p size of 0 to 4 bytes.
604-
static void emitMemCpy(IRGenFunction &IGF, Address to, Address from,
605-
llvm::Value *size) {
606-
emitSpecializedMemOperation(IGF,
607-
[=](IRBuilder &B, Size numBytes) {
608-
B.CreateMemCpy(to, from, numBytes);
609-
},
610-
size);
611-
}
612-
613651
void FixedTypeInfo::storeEnumTagSinglePayload(IRGenFunction &IGF,
614652
llvm::Value *whichCase,
615653
llvm::Value *numEmptyCases,
@@ -679,7 +717,7 @@ void irgen::storeFixedTypeEnumTagSinglePayload(IRGenFunction &IGF,
679717
// We are the payload or fit within the extra inhabitants.
680718
Builder.emitBlock(isPayloadOrInhabitantCaseBB);
681719
// Zero the tag bits.
682-
emitMemZero(IGF, extraTagBitsAddr, numExtraTagBytes);
720+
emitSetTag(IGF, extraTagBitsAddr, zero, numExtraTagBytes);
683721
isPayloadOrInhabitantCaseBB = Builder.GetInsertBlock();
684722
auto *storeInhabitantBB = llvm::BasicBlock::Create(Ctx);
685723
auto *returnBB = llvm::BasicBlock::Create(Ctx);
@@ -729,21 +767,28 @@ void irgen::storeFixedTypeEnumTagSinglePayload(IRGenFunction &IGF,
729767
payloadIndex->addIncoming(caseIndex, payloadGE4BB);
730768
payloadIndex->addIncoming(payloadIndex0, payloadLT4BB);
731769

732-
Address payloadIndexAddr = IGF.createAlloca(int32Ty, Alignment(4));
733-
Builder.CreateStore(payloadIndex, payloadIndexAddr);
734-
Address extraTagIndexAddr = IGF.createAlloca(int32Ty, Alignment(4));
735-
Builder.CreateStore(extraTagIndex, extraTagIndexAddr);
736-
// TODO: big endian
737-
Builder.CreateMemCpy(
738-
valueAddr,
739-
Builder.CreateBitCast(payloadIndexAddr, IGM.Int8PtrTy),
740-
std::min(Size(4U), fixedSize));
741-
Address extraZeroAddr = Builder.CreateConstByteArrayGEP(valueAddr, Size(4));
742-
if (fixedSize > Size(4))
743-
Builder.CreateMemSet(
744-
extraZeroAddr, llvm::ConstantInt::get(IGM.Int8Ty, 0),
745-
Builder.CreateSub(size, llvm::ConstantInt::get(size->getType(), 4)));
746-
emitMemCpy(IGF, extraTagBitsAddr, extraTagIndexAddr, numExtraTagBytes);
770+
if (fixedSize > Size(0)) {
771+
// Write the value into the first pointer-sized (or smaller)
772+
// 'chunk' of the payload.
773+
// The size of the chunk does not have to be a power of 2.
774+
Size limit = IGM.getPointerSize();
775+
auto *chunkType = Builder.getIntNTy(
776+
std::min(fixedSize, limit).getValueInBits());
777+
Builder.CreateStore(
778+
Builder.CreateZExtOrTrunc(payloadIndex, chunkType),
779+
Builder.CreateBitCast(valueAddr, chunkType->getPointerTo()));
780+
781+
// Zero the remainder of the payload.
782+
if (fixedSize > limit) {
783+
auto zeroAddr = Builder.CreateConstByteArrayGEP(valueAddr, limit);
784+
auto zeroSize = Builder.CreateSub(
785+
size,
786+
llvm::ConstantInt::get(size->getType(), limit.getValue()));
787+
Builder.CreateMemSet(zeroAddr, Builder.getInt8(0), zeroSize);
788+
}
789+
}
790+
// Write to the extra tag bytes, if any.
791+
emitSetTag(IGF, extraTagBitsAddr, extraTagIndex, numExtraTagBytes);
747792
Builder.CreateBr(returnBB);
748793

749794
Builder.emitBlock(returnBB);

0 commit comments

Comments
 (0)