Remove ldc.arrayinit & streamline _d_array_slice_copy/_d_arraycast_len with upstream

This commit is contained in:
Martin Kinkelin 2019-01-07 14:42:19 +01:00
parent b223888c19
commit 33cadfaca6
10 changed files with 106 additions and 94 deletions

View file

@ -186,18 +186,27 @@ static Type *DtoArrayElementType(Type *arrayType) {
////////////////////////////////////////////////////////////////////////////////
static void copySlice(Loc &loc, LLValue *dstarr, LLValue *sz1, LLValue *srcarr,
LLValue *sz2, bool knownInBounds) {
static LLValue *computeSize(LLValue *length, size_t elementSize) {
return elementSize == 1
? length
: gIR->ir->CreateMul(length, DtoConstSize_t(elementSize));
};
static void copySlice(Loc &loc, LLValue *dstarr, LLValue *dstlen, LLValue *srcarr,
LLValue *srclen, size_t elementSize, bool knownInBounds) {
const bool checksEnabled =
global.params.useAssert == CHECKENABLEon || gIR->emitArrayBoundsChecks();
if (checksEnabled && !knownInBounds) {
LLValue *fn = getRuntimeFunction(loc, gIR->module, "_d_array_slice_copy");
gIR->CreateCallOrInvoke(fn, dstarr, sz1, srcarr, sz2);
gIR->CreateCallOrInvoke(
fn, {dstarr, dstlen, srcarr, srclen, DtoConstSize_t(elementSize)}, "",
/*isNothrow=*/true);
} else {
// We might have dstarr == srcarr at compile time, but as long as
// sz1 == 0 at runtime, this would probably still be legal (the C spec
// is unclear here).
DtoMemCpy(dstarr, srcarr, sz1);
LLValue *size = computeSize(dstlen, elementSize);
DtoMemCpy(dstarr, srcarr, size);
}
}
@ -245,12 +254,6 @@ void DtoArrayAssign(Loc &loc, DValue *lhs, DValue *rhs, int op,
LLValue *lhsPtr = DtoBitCast(realLhsPtr, getVoidPtrType());
LLValue *lhsLength = DtoArrayLen(lhs);
auto computeSize = [](LLValue *length, size_t elementSize) {
return elementSize == 1
? length
: gIR->ir->CreateMul(length, DtoConstSize_t(elementSize));
};
// Be careful to handle void arrays correctly when modifying this (see tests
// for DMD issue 7493).
// TODO: This should use AssignExp::memset.
@ -268,15 +271,14 @@ void DtoArrayAssign(Loc &loc, DValue *lhs, DValue *rhs, int op,
if (!needsDestruction && !needsPostblit) {
// fast version
const size_t elementSize = getTypeAllocSize(DtoMemType(elemType));
LLValue *lhsSize = computeSize(lhsLength, elementSize);
if (rhs->isNull()) {
LLValue *lhsSize = computeSize(lhsLength, elementSize);
DtoMemSetZero(lhsPtr, lhsSize);
} else {
LLValue *rhsSize = computeSize(rhsLength, elementSize);
const bool knownInBounds =
isConstructing || (t->ty == Tsarray && t2->ty == Tsarray);
copySlice(loc, lhsPtr, lhsSize, rhsPtr, rhsSize, knownInBounds);
copySlice(loc, lhsPtr, lhsLength, rhsPtr, rhsLength, elementSize,
knownInBounds);
}
} else if (isConstructing) {
LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_arrayctor");
@ -912,7 +914,7 @@ DSliceValue *DtoCatArrays(Loc &loc, Type *arrayType, Expression *exp1,
}
auto newArray =
gIR->funcGen().callOrInvoke(fn, args, ".appendedArray").getInstruction();
gIR->CreateCallOrInvoke(fn, args, ".appendedArray").getInstruction();
return getSlice(arrayType, newArray);
}
@ -985,7 +987,7 @@ LLValue *DtoArrayEqCmp_impl(Loc &loc, const char *func, DValue *l,
args.push_back(DtoBitCast(tival, fn->getFunctionType()->getParamType(2)));
}
return gIR->funcGen().callOrInvoke(fn, args).getInstruction();
return gIR->CreateCallOrInvoke(fn, args).getInstruction();
}
/// When `true` is returned, the type can be compared using `memcmp`.
@ -1158,10 +1160,10 @@ LLValue *DtoArrayCastLength(Loc &loc, LLValue *len, LLType *elemty,
return len;
}
LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_array_cast_len");
LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_arraycast_len");
return gIR
->CreateCallOrInvoke(fn, len, LLConstantInt::get(DtoSize_t(), esz, false),
LLConstantInt::get(DtoSize_t(), nsz, false))
->CreateCallOrInvoke(fn, {len, DtoConstSize_t(esz), DtoConstSize_t(nsz)},
"", /*isNothrow=*/true)
.getInstruction();
}

View file

@ -102,3 +102,44 @@ llvm::BasicBlock *SwitchCaseTargets::getOrCreate(Statement *stmt,
FuncGenState::FuncGenState(IrFunction &irFunc, IRState &irs)
: irFunc(irFunc), scopes(irs), jumpTargets(scopes), switchTargets(),
irs(irs) {}
llvm::CallSite FuncGenState::callOrInvoke(llvm::Value *callee,
llvm::ArrayRef<llvm::Value *> args,
const char *name, bool isNothrow) {
// If this is a direct call, we might be able to use the callee attributes
// to our advantage.
llvm::Function *calleeFn = llvm::dyn_cast<llvm::Function>(callee);
// Ignore 'nothrow' if there are active catch blocks handling non-Exception
// Throwables.
if (isNothrow && scopes.isCatchingNonExceptions())
isNothrow = false;
// Intrinsics don't support invoking and 'nounwind' functions don't need it.
const bool doesNotThrow =
isNothrow ||
(calleeFn && (calleeFn->isIntrinsic() || calleeFn->doesNotThrow()));
// calls inside a funclet must be annotated with its value
llvm::SmallVector<llvm::OperandBundleDef, 2> BundleList;
if (doesNotThrow || scopes.empty()) {
llvm::CallInst *call = irs.ir->CreateCall(callee, args, BundleList, name);
if (calleeFn) {
call->setAttributes(calleeFn->getAttributes());
}
return call;
}
llvm::BasicBlock *landingPad = scopes.getLandingPad();
llvm::BasicBlock *postinvoke = irs.insertBB("postinvoke");
llvm::InvokeInst *invoke = irs.ir->CreateInvoke(
callee, postinvoke, landingPad, args, BundleList, name);
if (calleeFn) {
invoke->setAttributes(calleeFn->getAttributes());
}
irs.scope() = IRScope(postinvoke);
return invoke;
}

View file

@ -200,51 +200,10 @@ public:
/// Emits a call or invoke to the given callee, depending on whether there
/// are catches/cleanups active or not.
template <typename T>
llvm::CallSite callOrInvoke(llvm::Value *callee, const T &args,
llvm::CallSite callOrInvoke(llvm::Value *callee,
llvm::ArrayRef<llvm::Value *> args,
const char *name = "", bool isNothrow = false);
private:
IRState &irs;
};
template <typename T>
llvm::CallSite FuncGenState::callOrInvoke(llvm::Value *callee, const T &args,
const char *name, bool isNothrow) {
// If this is a direct call, we might be able to use the callee attributes
// to our advantage.
llvm::Function *calleeFn = llvm::dyn_cast<llvm::Function>(callee);
// Ignore 'nothrow' if there are active catch blocks handling non-Exception
// Throwables.
if (isNothrow && scopes.isCatchingNonExceptions())
isNothrow = false;
// Intrinsics don't support invoking and 'nounwind' functions don't need it.
const bool doesNotThrow =
isNothrow ||
(calleeFn && (calleeFn->isIntrinsic() || calleeFn->doesNotThrow()));
// calls inside a funclet must be annotated with its value
llvm::SmallVector<llvm::OperandBundleDef, 2> BundleList;
if (doesNotThrow || scopes.empty()) {
llvm::CallInst *call = irs.ir->CreateCall(callee, args, BundleList, name);
if (calleeFn) {
call->setAttributes(calleeFn->getAttributes());
}
return call;
}
llvm::BasicBlock *landingPad = scopes.getLandingPad();
llvm::BasicBlock *postinvoke = irs.insertBB("postinvoke");
llvm::InvokeInst *invoke = irs.ir->CreateInvoke(
callee, postinvoke, landingPad, args, BundleList, name);
if (calleeFn) {
invoke->setAttributes(calleeFn->getAttributes());
}
irs.scope() = IRScope(postinvoke);
return invoke;
}

View file

@ -87,34 +87,35 @@ llvm::BasicBlock *IRState::insertBB(const llvm::Twine &name) {
}
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, const char *Name) {
LLSmallVector<LLValue *, 1> args;
return funcGen().callOrInvoke(Callee, args, Name);
return funcGen().callOrInvoke(Callee, {}, Name);
}
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee,
llvm::ArrayRef<LLValue *> Args,
const char *Name, bool isNothrow) {
return funcGen().callOrInvoke(Callee, Args, Name, isNothrow);
}
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
const char *Name) {
LLValue *args[] = {Arg1};
return funcGen().callOrInvoke(Callee, args, Name);
return funcGen().callOrInvoke(Callee, {Arg1}, Name);
}
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
LLValue *Arg2, const char *Name) {
LLValue *args[] = {Arg1, Arg2};
return funcGen().callOrInvoke(Callee, args, Name);
return CreateCallOrInvoke(Callee, {Arg1, Arg2}, Name);
}
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
LLValue *Arg2, LLValue *Arg3,
const char *Name) {
LLValue *args[] = {Arg1, Arg2, Arg3};
return funcGen().callOrInvoke(Callee, args, Name);
return CreateCallOrInvoke(Callee, {Arg1, Arg2, Arg3}, Name);
}
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
LLValue *Arg2, LLValue *Arg3,
LLValue *Arg4, const char *Name) {
LLValue *args[] = {Arg1, Arg2, Arg3, Arg4};
return funcGen().callOrInvoke(Callee, args, Name);
return CreateCallOrInvoke(Callee, {Arg1, Arg2, Arg3, Arg4}, Name);
}
bool IRState::isMainFunc(const IrFunction *func) const {

View file

@ -158,6 +158,10 @@ public:
// create a call or invoke, depending on the landing pad info
llvm::CallSite CreateCallOrInvoke(LLValue *Callee, const char *Name = "");
llvm::CallSite CreateCallOrInvoke(LLValue *Callee,
llvm::ArrayRef<LLValue *> Args,
const char *Name = "",
bool isNothrow = false);
llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
const char *Name = "");
llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,

View file

@ -293,7 +293,7 @@ void DtoAssert(Module *M, Loc &loc, DValue *msg) {
args.push_back(DtoConstUint(loc.linnum));
// call
gIR->funcGen().callOrInvoke(fn, args);
gIR->CreateCallOrInvoke(fn, args);
// after assert is always unreachable
gIR->ir->CreateUnreachable();
@ -333,7 +333,7 @@ void DtoCAssert(Module *M, Loc &loc, LLValue *msg) {
args.push_back(line);
}
gIR->funcGen().callOrInvoke(fn, args);
gIR->CreateCallOrInvoke(fn, args);
gIR->ir->CreateUnreachable();
}

View file

@ -20,6 +20,7 @@
#endif
#include "gen/passes/Passes.h"
#include "gen/tollvm.h"
#include "gen/runtime.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringMap.h"
@ -168,7 +169,7 @@ struct LLVM_LIBRARY_VISIBILITY ArraySetLengthOpt : public LibCallOptimization {
struct LLVM_LIBRARY_VISIBILITY ArrayCastLenOpt : public LibCallOptimization {
Value *CallOptimizer(Function *Callee, CallInst *CI,
IRBuilder<> &B) override {
// Verify we have a reasonable prototype for _d_array_cast_len
// Verify we have a reasonable prototype for _d_arraycast_len
const FunctionType *FT = Callee->getFunctionType();
const llvm::Type *RetTy = FT->getReturnType();
if (Callee->arg_size() != 3 || !isa<IntegerType>(RetTy) ||
@ -261,27 +262,28 @@ struct LLVM_LIBRARY_VISIBILITY ArraySliceCopyOpt : public LibCallOptimization {
// Verify we have a reasonable prototype for _d_array_slice_copy
const FunctionType *FT = Callee->getFunctionType();
const llvm::Type *VoidPtrTy = PointerType::getUnqual(B.getInt8Ty());
if (Callee->arg_size() != 4 || FT->getReturnType() != B.getVoidTy() ||
if (Callee->arg_size() != 5 || FT->getReturnType() != B.getVoidTy() ||
FT->getParamType(0) != VoidPtrTy ||
!isa<IntegerType>(FT->getParamType(1)) ||
FT->getParamType(2) != VoidPtrTy ||
FT->getParamType(3) != FT->getParamType(1)) {
FT->getParamType(3) != FT->getParamType(1) ||
FT->getParamType(4) != FT->getParamType(1)) {
return nullptr;
}
Value *Size = CI->getOperand(1);
Value *DstLength = CI->getOperand(1);
// Check the lengths match
if (CI->getOperand(3) != Size) {
if (CI->getOperand(3) != DstLength) {
return nullptr;
}
// Assume unknown size unless we have constant size (that fits in an uint)
unsigned Sz = ~0U;
if (ConstantInt *Int = dyn_cast<ConstantInt>(Size)) {
if (Int->getValue().isIntN(32)) {
Sz = Int->getValue().getZExtValue();
}
const auto ElemSz = llvm::cast<ConstantInt>(CI->getOperand(4));
// Assume unknown size unless we have constant length
std::uint64_t Sz = llvm::MemoryLocation::UnknownSize;
if (ConstantInt *Int = dyn_cast<ConstantInt>(DstLength)) {
Sz = (Int->getValue() * ElemSz->getValue()).getZExtValue();
}
// Check if the pointers may alias
@ -291,6 +293,9 @@ struct LLVM_LIBRARY_VISIBILITY ArraySliceCopyOpt : public LibCallOptimization {
// Equal length and the pointers definitely don't alias, so it's safe to
// replace the call with memcpy
auto Size = Sz != llvm::MemoryLocation::UnknownSize
? DtoConstSize_t(Sz)
: B.CreateMul(DstLength, ElemSz);
return EmitMemCpy(CI->getOperand(0), CI->getOperand(2), Size, 1, B);
}
};
@ -345,9 +350,9 @@ FunctionPass *createSimplifyDRuntimeCalls() {
/// we know.
void SimplifyDRuntimeCalls::InitOptimizations() {
// Some array-related optimizations
Optimizations["_d_arraycast_len"] = &ArrayCastLen;
Optimizations["_d_arraysetlengthT"] = &ArraySetLength;
Optimizations["_d_arraysetlengthiT"] = &ArraySetLength;
Optimizations["_d_array_cast_len"] = &ArrayCastLen;
Optimizations["_d_array_slice_copy"] = &ArraySliceCopy;
/* Delete calls to runtime functions which aren't needed if their result is

View file

@ -69,12 +69,12 @@ static void checkForImplicitGCCall(const Loc &loc, const char *name) {
"_aaValues",
"_d_allocmemory",
"_d_allocmemoryT",
"_d_array_cast_len",
"_d_array_slice_copy",
"_d_arrayappendT",
"_d_arrayappendcTX",
"_d_arrayappendcd",
"_d_arrayappendwd",
"_d_arraycast_len",
"_d_arraycatT",
"_d_arraycatnTX",
"_d_arraysetlengthT",
@ -650,9 +650,10 @@ static void buildRuntimeModule() {
// array slice copy when assertions are on!
// void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t
// srclen)
// srclen, size_t elemsz)
createFwdDecl(LINKc, voidTy, {"_d_array_slice_copy"},
{voidPtrTy, sizeTy, voidPtrTy, sizeTy}, {}, Attr_1_3_NoCapture);
{voidPtrTy, sizeTy, voidPtrTy, sizeTy, sizeTy}, {},
Attr_1_3_NoCapture);
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
@ -691,8 +692,8 @@ static void buildRuntimeModule() {
//////////////////////////////////////////////////////////////////////////////
// fixes the length for dynamic array casts
// size_t _d_array_cast_len(size_t len, size_t elemsz, size_t newelemsz)
createFwdDecl(LINKc, sizeTy, {"_d_array_cast_len"}, {sizeTy, sizeTy, sizeTy},
// size_t _d_arraycast_len(size_t len, size_t elemsz, size_t newelemsz)
createFwdDecl(LINKc, sizeTy, {"_d_arraycast_len"}, {sizeTy, sizeTy, sizeTy},
{}, Attr_ReadNone);
//////////////////////////////////////////////////////////////////////////////

View file

@ -886,8 +886,7 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval,
}
// call the function
LLCallSite call =
gIR->funcGen().callOrInvoke(callable, args, "", tf->isnothrow);
LLCallSite call = gIR->CreateCallOrInvoke(callable, args, "", tf->isnothrow);
// PGO: Insert instrumentation or attach profile metadata at indirect call
// sites.

@ -1 +1 @@
Subproject commit 1ec2190a1d02de2c043dc8ef6aeab5c0e5e86cdd
Subproject commit 424f7ed10f0529b92f76eb843d83d146c117cbed