mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-06 10:57:35 +03:00
Remove ldc.arrayinit & streamline _d_array_slice_copy/_d_arraycast_len with upstream
This commit is contained in:
parent
b223888c19
commit
33cadfaca6
10 changed files with 106 additions and 94 deletions
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue