Fix array casts with constant source lengths

The `__ArrayCast` lowering isn't (always?) done when casting a static
array to a slice with different element size.
So exploit constant source lengths and compute the target length at
compile-time.

This fixes compilable/ldc_github_421.d - a float[16] can be cast to a
float4[] with constant length 4.
This commit is contained in:
Martin Kinkelin 2019-04-25 00:12:58 +02:00
parent 0e2173bf82
commit 9469c6b5b8

View file

@ -1263,38 +1263,37 @@ DValue *DtoCastArray(Loc &loc, DValue *u, Type *to) {
LLValue *length = nullptr; LLValue *length = nullptr;
LLValue *ptr = nullptr; LLValue *ptr = nullptr;
if (fromtype->ty == Tsarray) { if (fromtype->ty == Tsarray) {
uinteger_t len = static_cast<TypeSArray *>(fromtype)->dim->toUInteger(); length = DtoConstSize_t(
length = DtoConstSize_t(len); static_cast<TypeSArray *>(fromtype)->dim->toUInteger());
ptr = DtoLVal(u); ptr = DtoLVal(u);
assert(isaPointer(ptr->getType()));
LLArrayType *arrty = isaArray(ptr->getType()->getContainedType(0));
if (arrty->getNumElements() * fromtype->nextOf()->size() %
totype->nextOf()->size() !=
0) {
error(loc,
"invalid cast from `%s` to `%s`, the element sizes don't line up",
fromtype->toChars(), totype->toChars());
fatal();
}
} else { } else {
length = DtoArrayLen(u); length = DtoArrayLen(u);
ptr = DtoArrayPtr(u); ptr = DtoArrayPtr(u);
} }
LLType *ptrty = DtoArrayType(totype)->getContainedType(1);
const auto fsize = fromtype->nextOf()->size(); const auto fsize = fromtype->nextOf()->size();
const auto tsize = totype->nextOf()->size(); const auto tsize = totype->nextOf()->size();
if (fsize != tsize) { if (fsize != tsize) {
if (fsize % tsize == 0) { if (auto constLength = isaConstantInt(length)) {
// set new length to `length * (fsize / tsize)` // compute new constant length: (constLength * fsize) / tsize
const auto totalSize = constLength->getZExtValue() * fsize;
if (totalSize % tsize != 0) {
error(loc,
"invalid cast from `%s` to `%s`, the element sizes don't "
"line up",
fromtype->toChars(), totype->toChars());
fatal();
}
length = DtoConstSize_t(totalSize / tsize);
} else if (fsize % tsize == 0) {
// compute new dynamic length: length * (fsize / tsize)
length = gIR->ir->CreateMul(length, DtoConstSize_t(fsize / tsize)); length = gIR->ir->CreateMul(length, DtoConstSize_t(fsize / tsize));
} else { } else {
llvm_unreachable("should have been lowered to `__ArrayCast`"); llvm_unreachable("should have been lowered to `__ArrayCast`");
} }
} }
LLType *ptrty = tolltype->getStructElementType(1);
return new DSliceValue(to, length, DtoBitCast(ptr, ptrty)); return new DSliceValue(to, length, DtoBitCast(ptr, ptrty));
} }