in libraries/value/src/LLVMContext.cpp [1042:1195]
void LLVMContext::CopyDataImpl(const Value& source, Value& destination)
{
if (destination.IsConstant())
{
if (source.IsConstant())
{
return _computeContext.CopyData(source, destination);
}
else
{
auto emittableDestination = EnsureEmittable(destination);
CopyDataImpl(source, emittableDestination);
destination.SetData(emittableDestination);
}
}
else
{
if (!TypeCompatible(destination, source))
{
throw InputException(InputExceptionErrors::typeMismatch);
}
enum class CopyType
{
DirectScalarPassThrough,
DirectScalarCopy,
Memory,
Reference
};
CopyType copyType{};
if (auto srcPtrLevel = source.PointerLevel(); srcPtrLevel < 0)
{
throw LogicException(LogicExceptionErrors::illegalState);
}
else if (srcPtrLevel == 0)
{
switch (destination.PointerLevel())
{
case 0:
copyType = CopyType::DirectScalarPassThrough;
break;
case 1:
copyType = CopyType::DirectScalarCopy;
break;
default:
throw LogicException(LogicExceptionErrors::illegalState);
}
if (source.GetLayout() != ScalarLayout || destination.GetLayout() != ScalarLayout)
{
throw InputException(InputExceptionErrors::invalidSize);
}
}
else if (srcPtrLevel == 1)
{
if (destination.PointerLevel() != 1)
{
throw LogicException(LogicExceptionErrors::illegalState);
}
else
{
if (destination.GetLayout() != source.GetLayout())
{
throw InputException(InputExceptionErrors::sizeMismatch);
}
copyType = CopyType::Memory;
}
}
else if (destination.PointerLevel() == srcPtrLevel)
{
assert(srcPtrLevel > 1);
if (source.IsConstant())
{
throw LogicException(LogicExceptionErrors::illegalState);
}
copyType = CopyType::Reference;
}
else
{
throw LogicException(LogicExceptionErrors::illegalState);
}
auto& irEmitter = _emitter.GetIREmitter();
auto destValue = ToLLVMValue(destination);
if (source.IsConstant())
{
// we're only copying active areas below. should we copy padded too?
auto& layout = source.GetLayout();
std::visit(
VariantVisitor{ [](Emittable) {},
[destValue, &irEmitter, &layout](auto&& data) {
ConstantForLoop(layout, [&](int offset) {
auto srcAtOffset = irEmitter.Literal(*(data + offset));
auto destOffset = irEmitter.Literal(offset);
auto destAtOffset = irEmitter.PointerOffset(destValue, destOffset);
(void)irEmitter.Store(destAtOffset, srcAtOffset);
});
} },
source.GetUnderlyingData());
}
else
{
auto srcValue = ToLLVMValue(source);
if (srcValue == destValue)
{
return;
}
switch (copyType)
{
case CopyType::DirectScalarPassThrough:
destination.SetData(Emittable{ srcValue });
break;
case CopyType::DirectScalarCopy:
{
auto destAtOffset = irEmitter.PointerOffset(destValue, irEmitter.Zero(VariableType::Int32));
irEmitter.Store(destAtOffset, srcValue);
break;
}
case CopyType::Memory:
if (auto& layout = source.GetLayout(); layout.IsContiguous())
{
assert(copyType == CopyType::Memory);
auto llvmType = srcValue->getType()->getContainedType(0);
auto primSize = irEmitter.SizeOf(llvmType);
auto memorySize = irEmitter.Literal(static_cast<int64_t>(layout.GetMemorySize() * primSize));
irEmitter.MemoryCopy(srcValue,
destValue,
memorySize);
}
else
{
ForImpl(
layout, [&](std::vector<Scalar> index) {
auto offsetSource = source.Offset(detail::CalculateOffset(source.GetLayout(), index));
auto offsetDest = destination.Offset(detail::CalculateOffset(destination.GetLayout(), index));
(void)irEmitter.Store(ToLLVMValue(offsetDest), irEmitter.Load(ToLLVMValue(offsetSource)));
},
"");
}
break;
case CopyType::Reference:
{
auto srcAtOffset = irEmitter.Load(srcValue);
irEmitter.Store(destValue, srcAtOffset);
destination.SetLayout(source.GetLayout());
break;
}
default:
throw LogicException(LogicExceptionErrors::illegalState);
}
}
}
}