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, static LLValue *computeSize(LLValue *length, size_t elementSize) {
LLValue *sz2, bool knownInBounds) { 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 = const bool checksEnabled =
global.params.useAssert == CHECKENABLEon || gIR->emitArrayBoundsChecks(); global.params.useAssert == CHECKENABLEon || gIR->emitArrayBoundsChecks();
if (checksEnabled && !knownInBounds) { if (checksEnabled && !knownInBounds) {
LLValue *fn = getRuntimeFunction(loc, gIR->module, "_d_array_slice_copy"); 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 { } else {
// We might have dstarr == srcarr at compile time, but as long as // 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 // sz1 == 0 at runtime, this would probably still be legal (the C spec
// is unclear here). // 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 *lhsPtr = DtoBitCast(realLhsPtr, getVoidPtrType());
LLValue *lhsLength = DtoArrayLen(lhs); 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 // Be careful to handle void arrays correctly when modifying this (see tests
// for DMD issue 7493). // for DMD issue 7493).
// TODO: This should use AssignExp::memset. // TODO: This should use AssignExp::memset.
@ -268,15 +271,14 @@ void DtoArrayAssign(Loc &loc, DValue *lhs, DValue *rhs, int op,
if (!needsDestruction && !needsPostblit) { if (!needsDestruction && !needsPostblit) {
// fast version // fast version
const size_t elementSize = getTypeAllocSize(DtoMemType(elemType)); const size_t elementSize = getTypeAllocSize(DtoMemType(elemType));
LLValue *lhsSize = computeSize(lhsLength, elementSize);
if (rhs->isNull()) { if (rhs->isNull()) {
LLValue *lhsSize = computeSize(lhsLength, elementSize);
DtoMemSetZero(lhsPtr, lhsSize); DtoMemSetZero(lhsPtr, lhsSize);
} else { } else {
LLValue *rhsSize = computeSize(rhsLength, elementSize);
const bool knownInBounds = const bool knownInBounds =
isConstructing || (t->ty == Tsarray && t2->ty == Tsarray); 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) { } else if (isConstructing) {
LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_arrayctor"); LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_arrayctor");
@ -912,7 +914,7 @@ DSliceValue *DtoCatArrays(Loc &loc, Type *arrayType, Expression *exp1,
} }
auto newArray = auto newArray =
gIR->funcGen().callOrInvoke(fn, args, ".appendedArray").getInstruction(); gIR->CreateCallOrInvoke(fn, args, ".appendedArray").getInstruction();
return getSlice(arrayType, newArray); 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))); 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`. /// 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; return len;
} }
LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_array_cast_len"); LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_arraycast_len");
return gIR return gIR
->CreateCallOrInvoke(fn, len, LLConstantInt::get(DtoSize_t(), esz, false), ->CreateCallOrInvoke(fn, {len, DtoConstSize_t(esz), DtoConstSize_t(nsz)},
LLConstantInt::get(DtoSize_t(), nsz, false)) "", /*isNothrow=*/true)
.getInstruction(); .getInstruction();
} }

View file

@ -102,3 +102,44 @@ llvm::BasicBlock *SwitchCaseTargets::getOrCreate(Statement *stmt,
FuncGenState::FuncGenState(IrFunction &irFunc, IRState &irs) FuncGenState::FuncGenState(IrFunction &irFunc, IRState &irs)
: irFunc(irFunc), scopes(irs), jumpTargets(scopes), switchTargets(), : irFunc(irFunc), scopes(irs), jumpTargets(scopes), switchTargets(),
irs(irs) {} 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 /// Emits a call or invoke to the given callee, depending on whether there
/// are catches/cleanups active or not. /// are catches/cleanups active or not.
template <typename T> llvm::CallSite callOrInvoke(llvm::Value *callee,
llvm::CallSite callOrInvoke(llvm::Value *callee, const T &args, llvm::ArrayRef<llvm::Value *> args,
const char *name = "", bool isNothrow = false); const char *name = "", bool isNothrow = false);
private: private:
IRState &irs; 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) { LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, const char *Name) {
LLSmallVector<LLValue *, 1> args; return funcGen().callOrInvoke(Callee, {}, Name);
return funcGen().callOrInvoke(Callee, args, 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, LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
const char *Name) { const char *Name) {
LLValue *args[] = {Arg1}; return funcGen().callOrInvoke(Callee, {Arg1}, Name);
return funcGen().callOrInvoke(Callee, args, Name);
} }
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
LLValue *Arg2, const char *Name) { LLValue *Arg2, const char *Name) {
LLValue *args[] = {Arg1, Arg2}; return CreateCallOrInvoke(Callee, {Arg1, Arg2}, Name);
return funcGen().callOrInvoke(Callee, args, Name);
} }
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
LLValue *Arg2, LLValue *Arg3, LLValue *Arg2, LLValue *Arg3,
const char *Name) { const char *Name) {
LLValue *args[] = {Arg1, Arg2, Arg3}; return CreateCallOrInvoke(Callee, {Arg1, Arg2, Arg3}, Name);
return funcGen().callOrInvoke(Callee, args, Name);
} }
LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, LLCallSite IRState::CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
LLValue *Arg2, LLValue *Arg3, LLValue *Arg2, LLValue *Arg3,
LLValue *Arg4, const char *Name) { LLValue *Arg4, const char *Name) {
LLValue *args[] = {Arg1, Arg2, Arg3, Arg4}; return CreateCallOrInvoke(Callee, {Arg1, Arg2, Arg3, Arg4}, Name);
return funcGen().callOrInvoke(Callee, args, Name);
} }
bool IRState::isMainFunc(const IrFunction *func) const { 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 // create a call or invoke, depending on the landing pad info
llvm::CallSite CreateCallOrInvoke(LLValue *Callee, const char *Name = ""); 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, llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1,
const char *Name = ""); const char *Name = "");
llvm::CallSite CreateCallOrInvoke(LLValue *Callee, LLValue *Arg1, 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)); args.push_back(DtoConstUint(loc.linnum));
// call // call
gIR->funcGen().callOrInvoke(fn, args); gIR->CreateCallOrInvoke(fn, args);
// after assert is always unreachable // after assert is always unreachable
gIR->ir->CreateUnreachable(); gIR->ir->CreateUnreachable();
@ -333,7 +333,7 @@ void DtoCAssert(Module *M, Loc &loc, LLValue *msg) {
args.push_back(line); args.push_back(line);
} }
gIR->funcGen().callOrInvoke(fn, args); gIR->CreateCallOrInvoke(fn, args);
gIR->ir->CreateUnreachable(); gIR->ir->CreateUnreachable();
} }

View file

@ -20,6 +20,7 @@
#endif #endif
#include "gen/passes/Passes.h" #include "gen/passes/Passes.h"
#include "gen/tollvm.h"
#include "gen/runtime.h" #include "gen/runtime.h"
#include "llvm/ADT/Statistic.h" #include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringMap.h"
@ -168,7 +169,7 @@ struct LLVM_LIBRARY_VISIBILITY ArraySetLengthOpt : public LibCallOptimization {
struct LLVM_LIBRARY_VISIBILITY ArrayCastLenOpt : public LibCallOptimization { struct LLVM_LIBRARY_VISIBILITY ArrayCastLenOpt : public LibCallOptimization {
Value *CallOptimizer(Function *Callee, CallInst *CI, Value *CallOptimizer(Function *Callee, CallInst *CI,
IRBuilder<> &B) override { 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 FunctionType *FT = Callee->getFunctionType();
const llvm::Type *RetTy = FT->getReturnType(); const llvm::Type *RetTy = FT->getReturnType();
if (Callee->arg_size() != 3 || !isa<IntegerType>(RetTy) || 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 // Verify we have a reasonable prototype for _d_array_slice_copy
const FunctionType *FT = Callee->getFunctionType(); const FunctionType *FT = Callee->getFunctionType();
const llvm::Type *VoidPtrTy = PointerType::getUnqual(B.getInt8Ty()); 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 || FT->getParamType(0) != VoidPtrTy ||
!isa<IntegerType>(FT->getParamType(1)) || !isa<IntegerType>(FT->getParamType(1)) ||
FT->getParamType(2) != VoidPtrTy || FT->getParamType(2) != VoidPtrTy ||
FT->getParamType(3) != FT->getParamType(1)) { FT->getParamType(3) != FT->getParamType(1) ||
FT->getParamType(4) != FT->getParamType(1)) {
return nullptr; return nullptr;
} }
Value *Size = CI->getOperand(1); Value *DstLength = CI->getOperand(1);
// Check the lengths match // Check the lengths match
if (CI->getOperand(3) != Size) { if (CI->getOperand(3) != DstLength) {
return nullptr; return nullptr;
} }
// Assume unknown size unless we have constant size (that fits in an uint) const auto ElemSz = llvm::cast<ConstantInt>(CI->getOperand(4));
unsigned Sz = ~0U;
if (ConstantInt *Int = dyn_cast<ConstantInt>(Size)) { // Assume unknown size unless we have constant length
if (Int->getValue().isIntN(32)) { std::uint64_t Sz = llvm::MemoryLocation::UnknownSize;
Sz = Int->getValue().getZExtValue(); if (ConstantInt *Int = dyn_cast<ConstantInt>(DstLength)) {
} Sz = (Int->getValue() * ElemSz->getValue()).getZExtValue();
} }
// Check if the pointers may alias // 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 // Equal length and the pointers definitely don't alias, so it's safe to
// replace the call with memcpy // 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); return EmitMemCpy(CI->getOperand(0), CI->getOperand(2), Size, 1, B);
} }
}; };
@ -345,9 +350,9 @@ FunctionPass *createSimplifyDRuntimeCalls() {
/// we know. /// we know.
void SimplifyDRuntimeCalls::InitOptimizations() { void SimplifyDRuntimeCalls::InitOptimizations() {
// Some array-related optimizations // Some array-related optimizations
Optimizations["_d_arraycast_len"] = &ArrayCastLen;
Optimizations["_d_arraysetlengthT"] = &ArraySetLength; Optimizations["_d_arraysetlengthT"] = &ArraySetLength;
Optimizations["_d_arraysetlengthiT"] = &ArraySetLength; Optimizations["_d_arraysetlengthiT"] = &ArraySetLength;
Optimizations["_d_array_cast_len"] = &ArrayCastLen;
Optimizations["_d_array_slice_copy"] = &ArraySliceCopy; Optimizations["_d_array_slice_copy"] = &ArraySliceCopy;
/* Delete calls to runtime functions which aren't needed if their result is /* 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", "_aaValues",
"_d_allocmemory", "_d_allocmemory",
"_d_allocmemoryT", "_d_allocmemoryT",
"_d_array_cast_len",
"_d_array_slice_copy", "_d_array_slice_copy",
"_d_arrayappendT", "_d_arrayappendT",
"_d_arrayappendcTX", "_d_arrayappendcTX",
"_d_arrayappendcd", "_d_arrayappendcd",
"_d_arrayappendwd", "_d_arrayappendwd",
"_d_arraycast_len",
"_d_arraycatT", "_d_arraycatT",
"_d_arraycatnTX", "_d_arraycatnTX",
"_d_arraysetlengthT", "_d_arraysetlengthT",
@ -650,9 +650,10 @@ static void buildRuntimeModule() {
// array slice copy when assertions are on! // array slice copy when assertions are on!
// void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t // 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"}, 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 // fixes the length for dynamic array casts
// size_t _d_array_cast_len(size_t len, size_t elemsz, size_t newelemsz) // size_t _d_arraycast_len(size_t len, size_t elemsz, size_t newelemsz)
createFwdDecl(LINKc, sizeTy, {"_d_array_cast_len"}, {sizeTy, sizeTy, sizeTy}, createFwdDecl(LINKc, sizeTy, {"_d_arraycast_len"}, {sizeTy, sizeTy, sizeTy},
{}, Attr_ReadNone); {}, Attr_ReadNone);
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////

View file

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

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