Beispiel #1
0
    void visit(const For *op) {
        do_indent();

        out << op->for_type << ' ' << simplify_var_name(op->name);

        // If the min or extent are constants, print them. At this
        // stage they're all variables.
        Expr min_val = op->min, extent_val = op->extent;
        const Variable *min_var = min_val.as<Variable>();
        const Variable *extent_var = extent_val.as<Variable>();
        if (min_var && constants.contains(min_var->name)) {
            min_val = constants.get(min_var->name);
        }

        if (extent_var && constants.contains(extent_var->name)) {
            extent_val = constants.get(extent_var->name);
        }

        if (extent_val.defined() && is_const(extent_val) &&
            min_val.defined() && is_const(min_val)) {
            Expr max_val = simplify(min_val + extent_val - 1);
            out << " in [" << min_val << ", " << max_val << "]";
        }

        out << ":\n";
        indent += 2;
        op->body.accept(this);
        indent -= 2;
    }
Beispiel #2
0
void Closure::pack_struct(llvm::Type *
#if LLVM_VERSION >= 37
                          type
#endif
                          ,
                          Value *dst,
                          const Scope<Value *> &src,
                          IRBuilder<> *builder) {
    // type, type of dst should be a pointer to a struct of the type returned by build_type
    int idx = 0;
    LLVMContext &context = builder->getContext();
    vector<string> nm = names();
    vector<llvm::Type*> ty = llvm_types(&context);
    for (size_t i = 0; i < nm.size(); i++) {
#if LLVM_VERSION >= 37
        Value *ptr = builder->CreateConstInBoundsGEP2_32(type, dst, 0, idx);
#else
        Value *ptr = builder->CreateConstInBoundsGEP2_32(dst, 0, idx);
#endif
        Value *val;
        if (!ends_with(nm[i], ".buffer") || src.contains(nm[i])) {
            val = src.get(nm[i]);
            if (val->getType() != ty[i]) {
                val = builder->CreateBitCast(val, ty[i]);
            }
        } else {
            // Skip over buffers not in the symbol table. They must not be needed.
            val = ConstantPointerNull::get(buffer_t->getPointerTo());
        }
        builder->CreateStore(val, ptr);
        idx++;
    }
}
Beispiel #3
0
    void visit(const Free *op) {
        int idx = get_func_id(op->name);

        AllocSize alloc = func_alloc_sizes.get(op->name);
        func_alloc_sizes.pop(op->name);

        IRMutator::visit(op);

        if (!is_zero(alloc.size)) {
            Expr profiler_pipeline_state = Variable::make(Handle(), "profiler_pipeline_state");

            if (!alloc.on_stack) {
                debug(3) << "  Free on heap: " << op->name << "(" << alloc.size << ") in pipeline " << pipeline_name << "\n";
                Expr set_task = Call::make(Int(32), "halide_profiler_memory_free",
                                           {profiler_pipeline_state, idx, alloc.size}, Call::Extern);
                stmt = Block::make(Evaluate::make(set_task), stmt);
            } else {
                const int64_t *int_size = as_const_int(alloc.size);
                internal_assert(int_size != nullptr);

                func_stack_current[idx] -= *int_size;
                debug(3) << "  Free on stack: " << op->name << "(" << alloc.size << ") in pipeline " << pipeline_name
                         << "; current: " << func_stack_current[idx] << "; peak: " << func_stack_peak[idx] << "\n";
            }
        }
    }
Beispiel #4
0
 void visit(const Variable *op) {
     if (scope.contains(op->name)) {
         expr = scope.get(op->name);
     } else {
         expr = op;
     }
 }
Beispiel #5
0
 void visit(const Variable *op) {
     if (op->name == var) {
         result = Monotonic::Increasing;
     } else if (scope.contains(op->name)) {
         result = scope.get(op->name);
     } else {
         result = Monotonic::Constant;
     }
 }
Beispiel #6
0
 void visit(const Variable *op) {
     if (op->name == var) {
         expr = make_one(op->type);
     } else if (scope.contains(op->name)) {
         expr = scope.get(op->name);
     } else {
         expr = make_zero(op->type);
     }
 }
Beispiel #7
0
void ModulusRemainder::visit(const Variable *op) {
    if (scope.contains(op->name)) {
        pair<int, int> mod_rem = scope.get(op->name);
        modulus = mod_rem.first;
        remainder = mod_rem.second;
    } else {
        modulus = 1;
        remainder = 0;
    }
}
void ComputeModulusRemainder::visit(const Variable *op) {
    if (scope.contains(op->name)) {
        ModulusRemainder mod_rem = scope.get(op->name);
        modulus = mod_rem.modulus;
        remainder = mod_rem.remainder;
    } else {
        modulus = 1;
        remainder = 0;
    }
}
Beispiel #9
0
 virtual void visit(const Variable *op) {
     if (op->name == var) {
         expr = replacement;
     } else if (scope.contains(op->name)) {
         // The type of a var may have changed. E.g. if
         // we're vectorizing across x we need to know the
         // type of y has changed in the following example:
         // let y = x + 1 in y*3
         expr = Variable::make(scope.get(op->name), op->name);
     } else {
         expr = op;
     }
 }
Beispiel #10
0
	llvm::Value * get(std::string name)
	{
		auto it = table.find(name);

		if (it != table.end()) {
			return it->second;
		}

        if (parent != nullptr) {
            return parent->get(name);
        } else {
            return nullptr;
        }
	}
Beispiel #11
0
    string var(const string &x) {
        int id;
        if (scope.contains(x)) {
            id = scope.get(x);
        } else {
            id = unique_id();
            scope.push(x, id);
        }

        std::stringstream s;
        s << "<b class='Variable Matched' id='" << id << "-" << unique_id() << "'>";
        s << x;
        s << "</b>";
        return s.str();
    }
void Closure::pack_struct(Value *dst, const Scope<Value *> &src, IRBuilder<> *builder) {
    // dst should be a pointer to a struct of the type returned by build_type
    int idx = 0;
    LLVMContext &context = builder->getContext();
    vector<string> nm = names();
    vector<llvm::Type*> ty = llvm_types(&context);
    for (size_t i = 0; i < nm.size(); i++) {
        if (!ends_with(nm[i], ".buffer") || src.contains(nm[i])) {
            Value *val = src.get(nm[i]);
            Value *ptr = builder->CreateConstInBoundsGEP2_32(dst, 0, idx);
            if (val->getType() != ty[i]) {
                val = builder->CreateBitCast(val, ty[i]);
            }
            builder->CreateStore(val, ptr);
        }
        idx++;
    }
}
Beispiel #13
0
    void visit(const Pipeline *op) {
        if (op->name != func.name()) {
            IRMutator::visit(op);
        } else {
            
            // We're interested in the case where exactly one of the
            // mins of the buffer depends on the loop_var, and none of
            // the extents do.
            string dim = "";
            Expr min, extent;

            for (size_t i = 0; i < func.args().size(); i++) {
                string min_name = func.name() + "." + func.args()[i] + ".min";
                string extent_name = func.name() + "." + func.args()[i] + ".extent";
                assert(scope.contains(min_name) && scope.contains(extent_name));
                Expr this_min = scope.get(min_name);
                Expr this_extent = scope.get(extent_name);

                if (expr_depends_on_var(this_extent, loop_var)) {
                    min = Expr();
                    extent = Expr();
                    break;
                }

                if (expr_depends_on_var(this_min, loop_var)) {
                    if (min.defined()) {
                        min = Expr();
                        extent = Expr();
                        break;
                    } else {
                        dim = func.args()[i];
                        min = this_min;
                        extent = this_extent;
                    }
                }
            }

            if (min.defined()) {
                // Ok, we've isolated a function, a dimension to slide along, and loop variable to slide over
                debug(2) << "Sliding " << func.name() << " over dimension " << dim << " along loop variable " << loop_var << "\n";
                
                Expr loop_var_expr = Variable::make(Int(32), loop_var);
                Expr steady_state = loop_var_expr > loop_min;

                // The new min is one beyond the max we reached on the last loop iteration
                Expr new_min = substitute(loop_var, loop_var_expr - 1, min + extent);
                // The new extent is the old extent shrunk by how much we trimmed off the min
                Expr new_extent = extent + min - new_min;

                new_min = Select::make(steady_state, new_min, min);
                new_extent = Select::make(steady_state, new_extent, extent);

                stmt = LetStmt::make(func.name() + "." + dim + ".extent", new_extent, op);
                stmt = LetStmt::make(func.name() + "." + dim + ".min", new_min, stmt);

            } else {
                debug(2) << "Could not perform sliding window optimization of " << func.name() << " over " << loop_var << "\n";
                stmt = op;
            }


        }
    }
Beispiel #14
0
    void visit(const ProducerConsumer *op) {
        if (op->name != func.name()) {
            IRMutator::visit(op);
        } else {

            stmt = op;

            // We're interested in the case where exactly one of the
            // dimensions of the buffer has a min/extent that depends
            // on the loop_var.
            string dim = "";
            int dim_idx = 0;
            Expr min_required, max_required;

            debug(3) << "Considering sliding " << func.name()
                     << " along loop variable " << loop_var << "\n"
                     << "Region provided:\n";

            string prefix = func.name() + ".s" + std::to_string(func.updates().size()) + ".";
            const std::vector<string> func_args = func.args();
            for (int i = 0; i < func.dimensions(); i++) {
                // Look up the region required of this function's last stage
                string var = prefix + func_args[i];
                internal_assert(scope.contains(var + ".min") && scope.contains(var + ".max"));
                Expr min_req = scope.get(var + ".min");
                Expr max_req = scope.get(var + ".max");
                min_req = expand_expr(min_req, scope);
                max_req = expand_expr(max_req, scope);

                debug(3) << func_args[i] << ":" << min_req << ", " << max_req  << "\n";
                if (expr_depends_on_var(min_req, loop_var) ||
                    expr_depends_on_var(max_req, loop_var)) {
                    if (!dim.empty()) {
                        dim = "";
                        min_required = Expr();
                        max_required = Expr();
                        break;
                    } else {
                        dim = func_args[i];
                        dim_idx = i;
                        min_required = min_req;
                        max_required = max_req;
                    }
                }
            }

            if (!min_required.defined()) {
                debug(3) << "Could not perform sliding window optimization of "
                         << func.name() << " over " << loop_var << " because either zero "
                         << "or many dimensions of the function dependended on the loop var\n";
                return;
            }

            // If the function is not pure in the given dimension, give up. We also
            // need to make sure that it is pure in all the specializations
            bool pure = true;
            for (const Definition &def : func.updates()) {
                pure = is_dim_always_pure(def, dim, dim_idx);
                if (!pure) {
                    break;
                }
            }
            if (!pure) {
                debug(3) << "Could not performance sliding window optimization of "
                         << func.name() << " over " << loop_var << " because the function "
                         << "scatters along the related axis.\n";
                return;
            }

            bool can_slide_up = false;
            bool can_slide_down = false;

            Monotonic monotonic_min = is_monotonic(min_required, loop_var);
            Monotonic monotonic_max = is_monotonic(max_required, loop_var);

            if (monotonic_min == Monotonic::Increasing ||
                monotonic_min == Monotonic::Constant) {
                can_slide_up = true;
            }

            if (monotonic_max == Monotonic::Decreasing ||
                monotonic_max == Monotonic::Constant) {
                can_slide_down = true;
            }

            if (!can_slide_up && !can_slide_down) {
                debug(3) << "Not sliding " << func.name()
                         << " over dimension " << dim
                         << " along loop variable " << loop_var
                         << " because I couldn't prove it moved monotonically along that dimension\n"
                         << "Min is " << min_required << "\n"
                         << "Max is " << max_required << "\n";
                return;
            }

            // Ok, we've isolated a function, a dimension to slide
            // along, and loop variable to slide over.
            debug(3) << "Sliding " << func.name()
                     << " over dimension " << dim
                     << " along loop variable " << loop_var << "\n";

            Expr loop_var_expr = Variable::make(Int(32), loop_var);

            Expr prev_max_plus_one = substitute(loop_var, loop_var_expr - 1, max_required) + 1;
            Expr prev_min_minus_one = substitute(loop_var, loop_var_expr - 1, min_required) - 1;

            // If there's no overlap between adjacent iterations, we shouldn't slide.
            if (can_prove(min_required >= prev_max_plus_one) ||
                can_prove(max_required <= prev_min_minus_one)) {
                debug(3) << "Not sliding " << func.name()
                         << " over dimension " << dim
                         << " along loop variable " << loop_var
                         << " there's no overlap in the region computed across iterations\n"
                         << "Min is " << min_required << "\n"
                         << "Max is " << max_required << "\n";
                return;
            }

            Expr new_min, new_max;
            if (can_slide_up) {
                new_min = select(loop_var_expr <= loop_min, min_required, likely(prev_max_plus_one));
                new_max = max_required;
            } else {
                new_min = min_required;
                new_max = select(loop_var_expr <= loop_min, max_required, likely(prev_min_minus_one));
            }

            Expr early_stages_min_required = new_min;
            Expr early_stages_max_required = new_max;

            debug(3) << "Sliding " << func.name() << ", " << dim << "\n"
                     << "Pushing min up from " << min_required << " to " << new_min << "\n"
                     << "Shrinking max from " << max_required << " to " << new_max << "\n";

            // Now redefine the appropriate regions required
            if (can_slide_up) {
                replacements[prefix + dim + ".min"] = new_min;
            } else {
                replacements[prefix + dim + ".max"] = new_max;
            }

            for (size_t i = 0; i < func.updates().size(); i++) {
                string n = func.name() + ".s" + std::to_string(i) + "." + dim;
                replacements[n + ".min"] = Variable::make(Int(32), prefix + dim + ".min");
                replacements[n + ".max"] = Variable::make(Int(32), prefix + dim + ".max");
            }

            // Ok, we have a new min/max required and we're going to
            // rewrite all the lets that define bounds required. Now
            // we need to additionally expand the bounds required of
            // the last stage to cover values produced by stages
            // before the last one. Because, e.g., an intermediate
            // stage may be unrolled, expanding its bounds provided.
            if (op->update.defined()) {
                Box b = box_provided(op->produce, func.name());
                merge_boxes(b, box_provided(op->update, func.name()));
                if (can_slide_up) {
                    string n = prefix + dim + ".min";
                    Expr var = Variable::make(Int(32), n);
                    stmt = LetStmt::make(n, min(var, b[dim_idx].min), stmt);
                } else {
                    string n = prefix + dim + ".max";
                    Expr var = Variable::make(Int(32), n);
                    stmt = LetStmt::make(n, max(var, b[dim_idx].max), stmt);
                }
            }


        }
    }