in libraries/value/src/LLVMContext.cpp [1421:1550]
Value LLVMContext::LogicalOperationImpl(ValueLogicalOperation op, Value source1, Value source2)
{
if (source1.GetLayout() != source2.GetLayout())
{
throw InputException(InputExceptionErrors::sizeMismatch);
}
if (source1.IsConstant() && source2.IsConstant())
{
return _computeContext.LogicalOperation(op, source1, source2);
}
auto comparisonOp = TypedComparison::none;
bool isFp = source1.IsFloatingPoint();
switch (op)
{
case ValueLogicalOperation::equality:
comparisonOp = isFp ? TypedComparison::equalsFloat : TypedComparison::equals;
break;
case ValueLogicalOperation::inequality:
comparisonOp = isFp ? TypedComparison::notEqualsFloat : TypedComparison::notEquals;
break;
case ValueLogicalOperation::greaterthan:
comparisonOp = isFp ? TypedComparison::greaterThanFloat : TypedComparison::greaterThan;
break;
case ValueLogicalOperation::greaterthanorequal:
comparisonOp = isFp ? TypedComparison::greaterThanOrEqualsFloat : TypedComparison::greaterThanOrEquals;
break;
case ValueLogicalOperation::lessthan:
comparisonOp = isFp ? TypedComparison::lessThanFloat : TypedComparison::lessThan;
break;
case ValueLogicalOperation::lessthanorequal:
comparisonOp = isFp ? TypedComparison::lessThanOrEqualsFloat : TypedComparison::lessThanOrEquals;
break;
}
Value returnValue = std::visit(
VariantVisitor{
[this,
comparisonOp,
&source1,
&source2](Emittable source1Data) -> Value {
// source1 is an Emittable type, so source2 can be constant or Emittable
return std::visit(
VariantVisitor{
[&, this](Emittable source2Data) -> Value {
auto& fn = this->GetFunctionEmitter();
auto result = fn.TrueBit();
ForImpl(
source1.GetLayout(),
[&](std::vector<Scalar> index) {
auto offsetSource1 = source1.Offset(detail::CalculateOffset(source1.GetLayout(), index));
auto offsetSource2 = source2.Offset(detail::CalculateOffset(source2.GetLayout(), index));
result = fn.LogicalAnd(
result,
fn.Comparison(
comparisonOp,
fn.Load(ToLLVMValue(offsetSource1)),
fn.Load(ToLLVMValue(offsetSource2))));
},
"");
return { Emittable{ result }, ScalarLayout };
},
[&, this](auto&& source2Data) -> Value {
using Type = std::remove_pointer_t<std::decay_t<decltype(source2Data)>>;
using CastType = std::conditional_t<std::is_same_v<Type, Boolean>, bool, Type>;
auto& fn = this->GetFunctionEmitter();
LLVMValue result = nullptr;
auto llvmOp1 = source1Data.GetDataAs<LLVMValue>();
if (source1.PointerLevel() == 0)
{
result = fn.Comparison(
comparisonOp,
llvmOp1,
fn.Literal(static_cast<CastType>(source2Data[0])));
}
else
{
result = fn.TrueBit();
ConstantForLoop(source1.GetLayout(), [&](const MemoryCoordinates& logicalCoordinates) {
auto source1Offset = source1.GetLayout().GetLogicalEntryOffset(logicalCoordinates);
auto source2Offset = source2.GetLayout().GetLogicalEntryOffset(logicalCoordinates);
result = fn.LogicalAnd(
result,
fn.Comparison(
comparisonOp,
fn.ValueAt(llvmOp1, source1Offset),
fn.Literal(static_cast<CastType>(source2Data[source2Offset]))));
});
}
return { Emittable{ result }, ScalarLayout };
} },
source2.GetUnderlyingData());
},
[this,
comparisonOp,
&source2Data = source2.GetUnderlyingData(),
&source1Layout = source1.GetLayout(),
&source2Layout = source2.GetLayout()](auto&& source1Data) -> Value {
// source1 is constant, so source2 has to be an Emittable type
using Type = std::remove_pointer_t<std::decay_t<decltype(source1Data)>>;
using CastType = std::conditional_t<std::is_same_v<Type, Boolean>, bool, Type>;
auto& fn = GetFunctionEmitter();
auto result = fn.TrueBit();
auto llvmOp2 = std::get<Emittable>(source2Data).GetDataAs<LLVMValue>();
ConstantForLoop(source1Layout, [&](const MemoryCoordinates& logicalCoordinates) {
auto source1Offset = source1Layout.GetLogicalEntryOffset(logicalCoordinates);
auto source2Offset = source2Layout.GetLogicalEntryOffset(logicalCoordinates);
result = fn.LogicalAnd(
result,
fn.Comparison(
comparisonOp,
fn.Literal(static_cast<CastType>(source1Data[source1Offset])),
fn.ValueAt(llvmOp2, source2Offset)));
});
return { Emittable{ result }, ScalarLayout };
} },
source1.GetUnderlyingData());
return returnValue;
}