diff --git a/src/analysis/LivenessAnalysis.cpp b/src/analysis/LivenessAnalysis.cpp index 690c4182..368796b6 100644 --- a/src/analysis/LivenessAnalysis.cpp +++ b/src/analysis/LivenessAnalysis.cpp @@ -25,7 +25,8 @@ FastSet LivenessAnalysis::analyzeLiveness(const intermediate::Inte if(instr->hasValueType(ValueType::LOCAL) && !instr->hasDecoration(vc4c::intermediate::InstructionDecorations::ELEMENT_INSERTION)) { - if(instr->hasConditionalExecution()) + if(instr->hasConditionalExecution() && + !instr->hasDecoration(intermediate::InstructionDecorations::ELEMENT_INSERTION)) { auto condReadIt = conditionalReads.find(instr->getOutput()->local); if(condReadIt != conditionalReads.end() && condReadIt->second == instr->conditional && diff --git a/src/analysis/ValueRange.cpp b/src/analysis/ValueRange.cpp index 79e421d9..d2bb5fc8 100644 --- a/src/analysis/ValueRange.cpp +++ b/src/analysis/ValueRange.cpp @@ -15,6 +15,7 @@ #include using namespace vc4c; +using namespace vc4c::analysis; using namespace vc4c::intermediate; static bool isUnsignedType(const DataType& type) @@ -183,299 +184,316 @@ std::string ValueRange::to_string() const throw CompilationError(CompilationStep::GENERAL, "Invalid range type", std::to_string(static_cast(type))); } -FastMap ValueRange::determineValueRanges(Method& method) +void ValueRange::update(const Optional& constant, const FastMap& ranges, + const intermediate::IntermediateInstruction* it, Method* method) { - FastMap ranges; - - for(const Parameter& param : method.parameters) + const Operation* op = dynamic_cast(it); + + // values set by built-ins + if(it && + (it->hasDecoration(InstructionDecorations::BUILTIN_GLOBAL_ID) || + it->hasDecoration(InstructionDecorations::BUILTIN_GLOBAL_OFFSET) || + it->hasDecoration(InstructionDecorations::BUILTIN_GLOBAL_SIZE) || + it->hasDecoration(InstructionDecorations::BUILTIN_GROUP_ID) || + it->hasDecoration(InstructionDecorations::BUILTIN_NUM_GROUPS))) { - ranges.emplace(¶m, param.type); + // is always positive + extendBoundaries(static_cast(0), std::numeric_limits::max()); } - - auto it = method.walkAllInstructions(); - while(!it.isEndOfMethod()) + else if(it && it->hasDecoration(InstructionDecorations::BUILTIN_LOCAL_ID)) { - if(it.has() && !it.has() && it->hasValueType(ValueType::LOCAL)) + int64_t maxID = 0; + if(method && method->metaData.isWorkGroupSizeSet()) { - ValueRange& range = ranges.emplace(it->getOutput()->local, it->getOutput()->local->type).first->second; - auto constant = it->precalculate(3); - const Operation* op = it.get(); - - // values set by built-ins - if(it->hasDecoration(InstructionDecorations::BUILTIN_GLOBAL_ID) || - it->hasDecoration(InstructionDecorations::BUILTIN_GLOBAL_OFFSET) || - it->hasDecoration(InstructionDecorations::BUILTIN_GLOBAL_SIZE) || - it->hasDecoration(InstructionDecorations::BUILTIN_GROUP_ID) || - it->hasDecoration(InstructionDecorations::BUILTIN_NUM_GROUPS)) - { - // is always positive - range.extendBoundaries(static_cast(0), std::numeric_limits::max()); - } - else if(it->hasDecoration(InstructionDecorations::BUILTIN_LOCAL_ID)) - { - int64_t maxID = 0; - if(method.metaData.isWorkGroupSizeSet()) - { - maxID = *std::max_element( - method.metaData.workGroupSizes.begin(), method.metaData.workGroupSizes.end()) - - 1; - } - else - maxID = 11; - range.extendBoundaries(0l, maxID); - } - else if(it->hasDecoration(InstructionDecorations::BUILTIN_LOCAL_SIZE)) - { - int64_t maxSize = 0; - if(method.metaData.isWorkGroupSizeSet()) - { - maxSize = - *std::max_element(method.metaData.workGroupSizes.begin(), method.metaData.workGroupSizes.end()); - } - else - maxSize = NUM_QPUS; - range.extendBoundaries(0l, maxSize); - } - else if(it->hasDecoration(InstructionDecorations::BUILTIN_WORK_DIMENSIONS)) + maxID = + *std::max_element(method->metaData.workGroupSizes.begin(), method->metaData.workGroupSizes.end()) - 1; + } + else + maxID = 11; + extendBoundaries(0l, maxID); + } + else if(it && it->hasDecoration(InstructionDecorations::BUILTIN_LOCAL_SIZE)) + { + int64_t maxSize = 0; + if(method && method->metaData.isWorkGroupSizeSet()) + { + maxSize = *std::max_element(method->metaData.workGroupSizes.begin(), method->metaData.workGroupSizes.end()); + } + else + maxSize = NUM_QPUS; + extendBoundaries(0l, maxSize); + } + else if(it && it->hasDecoration(InstructionDecorations::BUILTIN_WORK_DIMENSIONS)) + { + extendBoundaries(static_cast(1), static_cast(3)); + } + // loading of immediates/literals + else if(constant.ifPresent(toFunction(&Value::isLiteralValue))) + { + if(constant->type.isFloatingType()) + extendBoundaries(constant->getLiteralValue()->real(), constant->getLiteralValue()->real()); + else + extendBoundaries(std::min(static_cast(constant->getLiteralValue()->signedInt()), + static_cast(constant->getLiteralValue()->unsignedInt())), + std::max(static_cast(constant->getLiteralValue()->signedInt()), + static_cast(constant->getLiteralValue()->unsignedInt()))); + } + else if(constant && constant->hasType(ValueType::CONTAINER)) + { + if(constant->type.isFloatingType()) + { + double min = std::numeric_limits::max(); + double max = std::numeric_limits::min(); + for(const Value& element : constant->container.elements) { - range.extendBoundaries(static_cast(1), static_cast(3)); + min = std::min(min, static_cast(element.getLiteralValue()->real())); + max = std::max(max, static_cast(element.getLiteralValue()->real())); } - // loading of immediates/literals - else if(constant.ifPresent(toFunction(&Value::isLiteralValue))) + extendBoundaries(min, max); + } + else + { + int64_t min = std::numeric_limits::max(); + int64_t max = std::numeric_limits::min(); + for(const Value& element : constant->container.elements) { - if(constant->type.isFloatingType()) - range.extendBoundaries(constant->getLiteralValue()->real(), constant->getLiteralValue()->real()); - else - range.extendBoundaries(std::min(static_cast(constant->getLiteralValue()->signedInt()), - static_cast(constant->getLiteralValue()->unsignedInt())), - std::max(static_cast(constant->getLiteralValue()->signedInt()), - static_cast(constant->getLiteralValue()->unsignedInt()))); + min = std::min(min, + std::min(static_cast(element.getLiteralValue()->signedInt()), + static_cast(element.getLiteralValue()->unsignedInt()))); + max = std::max(max, + std::max(static_cast(element.getLiteralValue()->signedInt()), + static_cast(element.getLiteralValue()->unsignedInt()))); } - else if(constant && constant->hasType(ValueType::CONTAINER)) + extendBoundaries(min, max); + } + } + else if(constant && constant->hasRegister(REG_QPU_NUMBER)) + { + extendBoundaries(static_cast(0), static_cast(11)); + } + else if(constant && constant->hasRegister(REG_ELEMENT_NUMBER)) + { + extendBoundaries(static_cast(0), static_cast(NATIVE_VECTOR_SIZE) - 1); + } + else if(dynamic_cast(it) && it->assertArgument(0).hasType(ValueType::LOCAL) && + ranges.find(it->assertArgument(0).local) != ranges.end()) + { + // move -> copy range from source local (TODO: would need to link the ranges e.g. if source changes + // afterwards!) + const ValueRange& sourceRange = ranges.at(it->assertArgument(0).local); + extendBoundaries(sourceRange); + } + else if(op && op->op == OP_AND && it->readsLiteral()) + { + /* + * y = x & constant + * + * y is in range [0, constant] (unsigned) + */ + auto argIt = std::find_if(it->getArguments().begin(), it->getArguments().end(), + [](const Value& val) -> bool { return val.isLiteralValue(); }); + if(argIt == it->getArguments().end()) + throw CompilationError(CompilationStep::GENERAL, + "Failed to get literal argument for operation which reads a literal value", it->to_string()); + extendBoundaries(0, static_cast(argIt->getLiteralValue()->unsignedInt())); + } + else if(op && op->op == OP_CLZ) + { + /* + * y = clz x + * + * y is in range [0, 32] (unsigned) + */ + extendBoundaries(static_cast(0), static_cast(32)); + } + else if(op && (op->op == OP_FMAXABS || op->op == OP_FMINABS)) + { + /* + * y = fmaxabs/fminabs x, z + * + * y is in range [0.0, float_max] + */ + extendBoundaries(0.0, std::numeric_limits::max()); + } + else if(op && op->op == OP_SHR && it->readsLiteral()) + { + /* + * y = x >> constant + * + * y is in range [x.min >> constant, x.max >> constant] (unsigned) + */ + if(it->assertArgument(0).hasType(ValueType::LOCAL) && + ranges.find(it->assertArgument(0).local) != ranges.end() && it->assertArgument(1).isLiteralValue()) + { + const ValueRange& sourceRange = ranges.at(it->assertArgument(0).local); + int64_t offset = static_cast(it->assertArgument(1).getLiteralValue()->signedInt()); + // TODO not correct if min/max is negative + extendBoundaries( + sourceRange.getIntRange()->minValue >> offset, sourceRange.getIntRange()->maxValue >> offset); + } + + /* + * y = constant >> x + * + * y is in range [0, constant] (unsigned) + */ + if(it->assertArgument(0).isLiteralValue()) + { + extendBoundaries(0, static_cast(it->assertArgument(0).getLiteralValue()->unsignedInt())); + } + } + // general case for operations, only works if the used locals are only written once (otherwise, their range + // could change afterwards!) + else if(op && !it->getArguments().empty() && + (op->op == OP_ADD || op->op == OP_AND || op->op == OP_FADD || op->op == OP_FMAX || op->op == OP_FMAXABS || + op->op == OP_FMIN || op->op == OP_FMINABS || op->op == OP_FMUL || op->op == OP_FSUB || op->op == OP_ITOF || + op->op == OP_MAX || op->op == OP_MIN || op->op == OP_MUL24 || op->op == OP_SHR || op->op == OP_SUB) && + std::all_of(it->getArguments().begin(), it->getArguments().end(), + [](const Value& arg) -> bool { return arg.isLiteralValue() || (arg.getSingleWriter() != nullptr); })) + { + /* + * We have an operation (with a valid op-code) where all operands are either constants or locals which + * are written only once before (and therefore have a fixed range, that is already known) + */ + const Value& arg0 = op->getFirstArg(); + ValueRange firstRange(arg0.type); + if(arg0.isLiteralValue()) + { + if(arg0.type.isFloatingType()) + firstRange.extendBoundaries(arg0.getLiteralValue()->real(), arg0.getLiteralValue()->real()); + else + firstRange.extendBoundaries(std::min(static_cast(arg0.getLiteralValue()->signedInt()), + static_cast(arg0.getLiteralValue()->unsignedInt())), + std::min(static_cast(arg0.getLiteralValue()->signedInt()), + static_cast(arg0.getLiteralValue()->unsignedInt()))); + } + else if(arg0.hasType(ValueType::LOCAL) && ranges.find(arg0.local) != ranges.end()) + firstRange.extendBoundaries(ranges.at(arg0.local)); + + Value firstMin(TYPE_UNKNOWN); + Value firstMax(TYPE_UNKNOWN); + + if(arg0.type.isFloatingType()) + { + firstMin = Value(Literal(saturate(firstRange.getFloatRange()->minValue)), arg0.type); + firstMax = Value(Literal(saturate(firstRange.getFloatRange()->maxValue)), arg0.type); + } + else + { + firstMin = Value(Literal(saturate(firstRange.getIntRange()->minValue)), arg0.type); + firstMax = Value(Literal(saturate(firstRange.getIntRange()->maxValue)), arg0.type); + } + + Optional minVal = NO_VALUE; + Optional maxVal = NO_VALUE; + + if(it->getArguments().size() > 1) + { + const Value arg1 = op->assertArgument(1); + ValueRange secondRange(arg1.type); + if(arg1.isLiteralValue()) { - if(constant->type.isFloatingType()) - { - double min = std::numeric_limits::max(); - double max = std::numeric_limits::min(); - for(const Value& element : constant->container.elements) - { - min = std::min(min, static_cast(element.getLiteralValue()->real())); - max = std::max(max, static_cast(element.getLiteralValue()->real())); - } - range.extendBoundaries(min, max); - } + if(arg1.type.isFloatingType()) + secondRange.extendBoundaries(arg1.getLiteralValue()->real(), arg1.getLiteralValue()->real()); else - { - int64_t min = std::numeric_limits::max(); - int64_t max = std::numeric_limits::min(); - for(const Value& element : constant->container.elements) - { - min = std::min(min, - std::min(static_cast(element.getLiteralValue()->signedInt()), - static_cast(element.getLiteralValue()->unsignedInt()))); - max = std::max(max, - std::max(static_cast(element.getLiteralValue()->signedInt()), - static_cast(element.getLiteralValue()->unsignedInt()))); - } - range.extendBoundaries(min, max); - } - } - else if(constant && constant->hasRegister(REG_QPU_NUMBER)) - { - range.extendBoundaries(static_cast(0), static_cast(11)); - } - else if(constant && constant->hasRegister(REG_ELEMENT_NUMBER)) - { - range.extendBoundaries(static_cast(0), static_cast(NATIVE_VECTOR_SIZE) - 1); - } - else if(it.has() && it->assertArgument(0).hasType(ValueType::LOCAL) && - ranges.find(it->assertArgument(0).local) != ranges.end()) - { - // move -> copy range from source local (TODO: would need to link the ranges e.g. if source changes - // afterwards!) - const ValueRange& sourceRange = ranges.at(it->assertArgument(0).local); - range.extendBoundaries(sourceRange); - } - else if(op && op->op == OP_AND && it->readsLiteral()) - { - /* - * y = x & constant - * - * y is in range [0, constant] (unsigned) - */ - auto argIt = std::find_if(it->getArguments().begin(), it->getArguments().end(), - [](const Value& val) -> bool { return val.isLiteralValue(); }); - if(argIt == it->getArguments().end()) - throw CompilationError(CompilationStep::GENERAL, - "Failed to get literal argument for operation which reads a literal value", it->to_string()); - range.extendBoundaries(0, static_cast(argIt->getLiteralValue()->unsignedInt())); - } - else if(op && op->op == OP_CLZ) - { - /* - * y = clz x - * - * y is in range [0, 32] (unsigned) - */ - range.extendBoundaries(static_cast(0), static_cast(32)); - } - else if(op && (op->op == OP_FMAXABS || op->op == OP_FMINABS)) - { - /* - * y = fmaxabs/fminabs x, z - * - * y is in range [0.0, float_max] - */ - range.extendBoundaries(0.0, std::numeric_limits::max()); + secondRange.extendBoundaries(std::min(static_cast(arg1.getLiteralValue()->signedInt()), + static_cast(arg1.getLiteralValue()->unsignedInt())), + std::min(static_cast(arg1.getLiteralValue()->signedInt()), + static_cast(arg1.getLiteralValue()->unsignedInt()))); } - else if(op && op->op == OP_SHR && it->readsLiteral()) - { - /* - * y = x >> constant - * - * y is in range [x.min >> constant, x.max >> constant] (unsigned) - */ - if(it->assertArgument(0).hasType(ValueType::LOCAL) && - ranges.find(it->assertArgument(0).local) != ranges.end() && it->assertArgument(1).isLiteralValue()) - { - const ValueRange& sourceRange = ranges.at(it->assertArgument(0).local); - int64_t offset = static_cast(it->assertArgument(1).getLiteralValue()->signedInt()); - // TODO not correct if min/max is negative - range.extendBoundaries( - sourceRange.getIntRange()->minValue >> offset, sourceRange.getIntRange()->maxValue >> offset); - } - - /* - * y = constant >> x - * - * y is in range [0, constant] (unsigned) - */ - if(it->assertArgument(0).isLiteralValue()) - { - range.extendBoundaries( - 0, static_cast(it->assertArgument(0).getLiteralValue()->unsignedInt())); - } - } - // general case for operations, only works if the used locals are only written once (otherwise, their range - // could change afterwards!) - else if(op && !it->getArguments().empty() && - (op->op == OP_ADD || op->op == OP_AND || op->op == OP_FADD || op->op == OP_FMAX || - op->op == OP_FMAXABS || op->op == OP_FMIN || op->op == OP_FMINABS || op->op == OP_FMUL || - op->op == OP_FSUB || op->op == OP_ITOF || op->op == OP_MAX || op->op == OP_MIN || - op->op == OP_MUL24 || op->op == OP_SHR || op->op == OP_SUB) && - std::all_of(it->getArguments().begin(), it->getArguments().end(), [](const Value& arg) -> bool { - return arg.isLiteralValue() || (arg.getSingleWriter() != nullptr); - })) + else if(arg1.hasType(ValueType::LOCAL) && ranges.find(arg1.local) != ranges.end()) + secondRange.extendBoundaries(ranges.at(arg1.local)); + + Value secondMin(TYPE_UNKNOWN); + Value secondMax(TYPE_UNKNOWN); + + if(arg1.type.isFloatingType()) { - /* - * We have an operation (with a valid op-code) where all operands are either constants or locals which - * are written only once before (and therefore have a fixed range, that is already known) - */ - const Value& arg0 = op->getFirstArg(); - ValueRange firstRange(arg0.type); - if(arg0.isLiteralValue()) - { - if(arg0.type.isFloatingType()) - firstRange.extendBoundaries(arg0.getLiteralValue()->real(), arg0.getLiteralValue()->real()); - else - firstRange.extendBoundaries(std::min(static_cast(arg0.getLiteralValue()->signedInt()), - static_cast(arg0.getLiteralValue()->unsignedInt())), - std::min(static_cast(arg0.getLiteralValue()->signedInt()), - static_cast(arg0.getLiteralValue()->unsignedInt()))); - } - else if(arg0.hasType(ValueType::LOCAL) && ranges.find(arg0.local) != ranges.end()) - firstRange.extendBoundaries(ranges.at(arg0.local)); - - Value firstMin(TYPE_UNKNOWN); - Value firstMax(TYPE_UNKNOWN); - - if(arg0.type.isFloatingType()) - { - firstMin = Value(Literal(saturate(firstRange.getFloatRange()->minValue)), arg0.type); - firstMax = Value(Literal(saturate(firstRange.getFloatRange()->maxValue)), arg0.type); - } - else - { - firstMin = Value(Literal(saturate(firstRange.getIntRange()->minValue)), arg0.type); - firstMax = Value(Literal(saturate(firstRange.getIntRange()->maxValue)), arg0.type); - } - - Optional minVal = NO_VALUE; - Optional maxVal = NO_VALUE; - - if(it->getArguments().size() > 1) - { - const Value arg1 = op->assertArgument(1); - ValueRange secondRange(arg1.type); - if(arg1.isLiteralValue()) - { - if(arg1.type.isFloatingType()) - secondRange.extendBoundaries( - arg1.getLiteralValue()->real(), arg1.getLiteralValue()->real()); - else - secondRange.extendBoundaries( - std::min(static_cast(arg1.getLiteralValue()->signedInt()), - static_cast(arg1.getLiteralValue()->unsignedInt())), - std::min(static_cast(arg1.getLiteralValue()->signedInt()), - static_cast(arg1.getLiteralValue()->unsignedInt()))); - } - else if(arg1.hasType(ValueType::LOCAL) && ranges.find(arg1.local) != ranges.end()) - secondRange.extendBoundaries(ranges.at(arg1.local)); - - Value secondMin(TYPE_UNKNOWN); - Value secondMax(TYPE_UNKNOWN); - - if(arg1.type.isFloatingType()) - { - secondMin = Value(Literal(saturate(secondRange.getFloatRange()->minValue)), arg1.type); - secondMax = Value(Literal(saturate(secondRange.getFloatRange()->maxValue)), arg1.type); - } - else - { - secondMin = Value(Literal(saturate(secondRange.getIntRange()->minValue)), arg1.type); - secondMax = Value(Literal(saturate(secondRange.getIntRange()->maxValue)), arg1.type); - } - - minVal = op->op.calculate(firstMin, secondMin); - maxVal = op->op.calculate(firstMax, secondMax); - } - else - { - minVal = op->op.calculate(firstMin, NO_VALUE); - maxVal = op->op.calculate(firstMax, NO_VALUE); - } - - if(it->hasDecoration(InstructionDecorations::UNSIGNED_RESULT) && - !it->getOutput()->type.isFloatingType()) - { - if(minVal.ifPresent(toFunction(&Value::isLiteralValue))) - minVal = Value(Literal(std::max(minVal->getLiteralValue()->signedInt(), 0)), minVal->type); - if(maxVal.ifPresent(toFunction(&Value::isLiteralValue))) - maxVal = Value(Literal(std::max(maxVal->getLiteralValue()->signedInt(), 0)), minVal->type); - } - - if(minVal.ifPresent(toFunction(&Value::isLiteralValue)) && - maxVal.ifPresent(toFunction(&Value::isLiteralValue))) - { - if(it->getOutput()->type.isFloatingType()) - range.extendBoundaries(minVal->getLiteralValue()->real(), maxVal->getLiteralValue()->real()); - else - range.extendBoundaries(std::min(static_cast(minVal->getLiteralValue()->signedInt()), - static_cast(minVal->getLiteralValue()->unsignedInt())), - std::max(static_cast(maxVal->getLiteralValue()->signedInt()), - static_cast(maxVal->getLiteralValue()->unsignedInt()))); - } - else - // failed to pre-calculate the bounds - range.extendBoundariesToUnknown(isUnsignedType(it->getOutput()->type) || - it->hasDecoration(InstructionDecorations::UNSIGNED_RESULT)); + secondMin = Value(Literal(saturate(secondRange.getFloatRange()->minValue)), arg1.type); + secondMax = Value(Literal(saturate(secondRange.getFloatRange()->maxValue)), arg1.type); } else { - // any other operation, set to min/max? - range.extendBoundariesToUnknown(isUnsignedType(it->getOutput()->type) || - it->hasDecoration(InstructionDecorations::UNSIGNED_RESULT)); + secondMin = Value(Literal(saturate(secondRange.getIntRange()->minValue)), arg1.type); + secondMax = Value(Literal(saturate(secondRange.getIntRange()->maxValue)), arg1.type); } + + minVal = op->op.calculate(firstMin, secondMin); + maxVal = op->op.calculate(firstMax, secondMax); + } + else + { + minVal = op->op.calculate(firstMin, NO_VALUE); + maxVal = op->op.calculate(firstMax, NO_VALUE); + } + + if(it->hasDecoration(InstructionDecorations::UNSIGNED_RESULT) && !it->getOutput()->type.isFloatingType()) + { + if(minVal.ifPresent(toFunction(&Value::isLiteralValue))) + minVal = Value(Literal(std::max(minVal->getLiteralValue()->signedInt(), 0)), minVal->type); + if(maxVal.ifPresent(toFunction(&Value::isLiteralValue))) + maxVal = Value(Literal(std::max(maxVal->getLiteralValue()->signedInt(), 0)), minVal->type); + } + + if(minVal.ifPresent(toFunction(&Value::isLiteralValue)) && maxVal.ifPresent(toFunction(&Value::isLiteralValue))) + { + if(it->getOutput()->type.isFloatingType()) + extendBoundaries(minVal->getLiteralValue()->real(), maxVal->getLiteralValue()->real()); + else + extendBoundaries(std::min(static_cast(minVal->getLiteralValue()->signedInt()), + static_cast(minVal->getLiteralValue()->unsignedInt())), + std::max(static_cast(maxVal->getLiteralValue()->signedInt()), + static_cast(maxVal->getLiteralValue()->unsignedInt()))); + } + else + // failed to pre-calculate the bounds + extendBoundariesToUnknown( + isUnsignedType(it->getOutput()->type) || it->hasDecoration(InstructionDecorations::UNSIGNED_RESULT)); + } + else + { + // any other operation, set to min/max? + extendBoundariesToUnknown(it && + (isUnsignedType(it->getOutput()->type) || it->hasDecoration(InstructionDecorations::UNSIGNED_RESULT))); + } +} + +ValueRange ValueRange::getValueRange(const Value& val, Method* method) +{ + ValueRange range(val.type.isFloatingType(), true); + auto singleWriter = val.hasType(ValueType::LOCAL) ? val.local->getSingleWriter() : nullptr; + FastMap ranges; + if(singleWriter && dynamic_cast(singleWriter)) + { + const Value& src = dynamic_cast(singleWriter)->getSource(); + if(src.hasType(ValueType::LOCAL)) + { + auto& tmp = ranges.emplace(src.local, src.local->type).first->second; + tmp.update(NO_VALUE, ranges, src.local->getSingleWriter()); + } + } + range.update(val.isLiteralValue() ? Optional(val) : + (singleWriter ? singleWriter->precalculate(3) : Optional{}), + ranges, singleWriter); + return range; +} + +FastMap ValueRange::determineValueRanges(Method& method) +{ + FastMap ranges; + + for(const Parameter& param : method.parameters) + { + ranges.emplace(¶m, param.type); + } + + auto it = method.walkAllInstructions(); + while(!it.isEndOfMethod()) + { + if(it.has() && !it.has() && it->hasValueType(ValueType::LOCAL)) + { + ValueRange& range = ranges.emplace(it->getOutput()->local, it->getOutput()->local->type).first->second; + range.update(it->precalculate(3), ranges, it.get(), &method); } it.nextInMethod(); diff --git a/src/analysis/ValueRange.h b/src/analysis/ValueRange.h index eb121aac..b4f0dd7a 100644 --- a/src/analysis/ValueRange.h +++ b/src/analysis/ValueRange.h @@ -18,8 +18,14 @@ namespace vc4c struct DataType; class Local; class Method; + class Value; namespace intermediate + { + class IntermediateInstruction; + } + + namespace analysis { struct FloatRange { @@ -53,6 +59,7 @@ namespace vc4c std::string to_string() const; + static ValueRange getValueRange(const Value& val, Method* method = nullptr); static FastMap determineValueRanges(Method& method); private: @@ -74,9 +81,11 @@ namespace vc4c void extendBoundaries(int64_t newMin, int64_t newMax); void extendBoundaries(const ValueRange& other); void extendBoundariesToUnknown(bool isKnownToBeUnsigned = false); + void update(const Optional& constant, const FastMap& ranges, + const intermediate::IntermediateInstruction* it = nullptr, Method* method = nullptr); }; - } /* namespace intermediate */ + } /* namespace analysis */ } /* namespace vc4c */ #endif /* VC4C_VALUE_RANGE_H */ diff --git a/src/intrinsics/Intrinsics.cpp b/src/intrinsics/Intrinsics.cpp index cfa0747b..1c92c029 100644 --- a/src/intrinsics/Intrinsics.cpp +++ b/src/intrinsics/Intrinsics.cpp @@ -6,6 +6,7 @@ #include "Intrinsics.h" +#include "../analysis/ValueRange.h" #include "../intermediate/Helper.h" #include "../intermediate/TypeConversions.h" #include "../periphery/SFU.h" @@ -597,6 +598,15 @@ static InstructionWalker intrinsifyArithmetic(Method& method, InstructionWalker it.reset(new Operation(OP_MUL24, op->getOutput().value(), op->getFirstArg(), op->assertArgument(1), op->conditional, op->setFlags)); } + else if(std::all_of(op->getArguments().begin(), op->getArguments().end(), [](const Value& arg) -> bool { + return vc4c::analysis::ValueRange::getValueRange(arg).isUnsigned(); + })) + { + logging::debug() + << "Intrinsifying signed multiplication of purely unsigned values to unsigned multiplication" + << logging::endl; + it = intrinsifyUnsignedIntegerMultiplication(method, it, *op); + } else { it = intrinsifySignedIntegerMultiplication(method, it, *op); @@ -901,20 +911,24 @@ static InstructionWalker intrinsifyReadWorkGroupInfo(Method& method, Instruction it.nextInBlock(); it.emplace(new MoveOperation( it->getOutput().value(), method.findOrCreateLocal(TYPE_INT32, locals.at(0))->createReference(), COND_ZERO_SET)); + it->addDecorations(InstructionDecorations::ELEMENT_INSERTION); it.nextInBlock(); // dim == 1 -> return second value it.emplace((new Operation(OP_XOR, NOP_REGISTER, arg, INT_ONE))->setSetFlags(SetFlag::SET_FLAGS)); it.nextInBlock(); it.emplace(new MoveOperation( it->getOutput().value(), method.findOrCreateLocal(TYPE_INT32, locals.at(1))->createReference(), COND_ZERO_SET)); + it->addDecorations(InstructionDecorations::ELEMENT_INSERTION); it.nextInBlock(); // dim == 2 -> return third value it.emplace((new Operation(OP_XOR, NOP_REGISTER, arg, Value(Literal(static_cast(2)), TYPE_INT32))) ->setSetFlags(SetFlag::SET_FLAGS)); it.nextInBlock(); - return it.reset((new MoveOperation(it->getOutput().value(), - method.findOrCreateLocal(TYPE_INT32, locals.at(2))->createReference(), COND_ZERO_SET)) - ->addDecorations(decoration)); + it.reset((new MoveOperation(it->getOutput().value(), + method.findOrCreateLocal(TYPE_INT32, locals.at(2))->createReference(), COND_ZERO_SET)) + ->addDecorations(decoration)); + it->addDecorations(InstructionDecorations::ELEMENT_INSERTION); + return it; } static InstructionWalker intrinsifyReadWorkItemInfo(Method& method, InstructionWalker it, const Value& arg, diff --git a/src/optimization/Eliminator.cpp b/src/optimization/Eliminator.cpp index 5b7a93a0..967d3611 100644 --- a/src/optimization/Eliminator.cpp +++ b/src/optimization/Eliminator.cpp @@ -411,7 +411,11 @@ bool optimizations::propagateMoves(const Module& module, Method& method, const C auto it2 = it.copy().nextInBlock(); auto oldValue = op->getOutput().value(); const auto& newValue = op->getSource(); - while(!it2.isEndOfBlock()) + // only continue iterating as long as there is a read of the local left + FastSet remainingLocalReads = oldValue.hasType(ValueType::LOCAL) ? + oldValue.local->getUsers(LocalUse::Type::READER) : + FastSet{}; + while(!it2.isEndOfBlock() && !remainingLocalReads.empty()) { bool replacedThisInstruction = false; for(auto arg : it2->getArguments()) @@ -422,6 +426,7 @@ bool optimizations::propagateMoves(const Module& module, Method& method, const C replaced = true; replacedThisInstruction = true; it2->replaceValue(oldValue, newValue, LocalUse::Type::READER); + remainingLocalReads.erase(it2.get()); } }