StmtToHtml.cpp
#include "StmtToHtml.h"
#include "IROperator.h"
#include "IRVisitor.h"
#include "Module.h"
#include "Scope.h"
#include "Util.h"
#include <cstdio>
#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
namespace Halide {
namespace Internal {
using std::string;
namespace {
template<typename T>
std::string to_string(T value) {
std::ostringstream os;
os << value;
return os.str();
}
class StmtToHtml : public IRVisitor {
static const std::string css, js;
// This allows easier access to individual elements.
int id_count;
private:
std::ofstream stream;
int unique_id() {
return ++id_count;
}
// All spans and divs will have an id of the form "x-y", where x
// is shared among all spans/divs in the same context, and y is unique.
std::vector<int> context_stack;
string open_tag(const string &tag, const string &cls, int id = -1) {
std::stringstream s;
s << "<" << tag << " class='" << cls << "' id='";
if (id == -1) {
s << context_stack.back() << "-";
s << unique_id();
} else {
s << id;
}
s << "'>";
context_stack.push_back(unique_id());
return s.str();
}
string tag(const string &tag, const string &cls, const string &body, int id = -1) {
std::stringstream s;
s << open_tag(tag, cls, id);
s << body;
s << close_tag(tag);
return s.str();
}
string close_tag(const string &tag) {
context_stack.pop_back();
return "</" + tag + ">";
}
string open_span(const string &cls, int id = -1) {
return open_tag("span", cls, id);
}
string close_span() {
return close_tag("span");
}
string span(const string &cls, const string &body, int id = -1) {
return tag("span", cls, body, id);
}
string matched(const string &cls, const string &body, int id = -1) {
return span(cls + " Matched", body, id);
}
string matched(const string &body) {
return span("Matched", body);
}
string open_div(const string &cls, int id = -1) {
return open_tag("div", cls, id) + "\n";
}
string close_div() {
return close_tag("div") + "\n";
}
string open_line() {
return "<p class=WrapLine>";
}
string close_line() {
return "</p>";
}
string keyword(const string &x) {
return span("Keyword", x);
}
string type(const string &x) {
return span("Type", x);
}
string symbol(const string &x) {
return span("Symbol", x);
}
Scope<int> scope;
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 print_list(const std::vector<Expr> &args) {
for (size_t i = 0; i < args.size(); i++) {
if (i > 0) {
stream << matched(",") << " ";
}
print(args[i]);
}
}
void print_list(const string &l, const std::vector<Expr> &args, const string &r) {
stream << matched(l);
print_list(args);
stream << matched(r);
}
string open_expand_button(int id) {
std::stringstream button;
button << "<a class=ExpandButton onclick='return toggle(" << id << ");' href=_blank>"
<< "<div style='position:relative; width:0; height:0;'>"
<< "<div class=ShowHide style='display:none;' id=" << id << "-show"
<< "><i class='fa fa-plus-square-o'></i></div>"
<< "<div class=ShowHide id=" << id << "-hide"
<< "><i class='fa fa-minus-square-o'></i></div>"
<< "</div>";
return button.str();
}
string close_expand_button() {
return "</a>";
}
void visit(const IntImm *op) override {
stream << open_span("IntImm Imm");
stream << Expr(op);
stream << close_span();
}
void visit(const UIntImm *op) override {
stream << open_span("UIntImm Imm");
stream << Expr(op);
stream << close_span();
}
void visit(const FloatImm *op) override {
stream << open_span("FloatImm Imm");
stream << Expr(op);
stream << close_span();
}
void visit(const StringImm *op) override {
stream << open_span("StringImm");
stream << "\"";
for (unsigned char c : op->value) {
if (c >= ' ' && c <= '~' && c != '\\' && c != '"') {
stream << c;
} else {
stream << "\\";
switch (c) {
case '"':
stream << "\"";
break;
case '\\':
stream << "\\";
break;
case '\t':
stream << "t";
break;
case '\r':
stream << "r";
break;
case '\n':
stream << "n";
break;
default:
string hex_digits = "0123456789ABCDEF";
stream << "x" << hex_digits[c >> 4] << hex_digits[c & 0xf];
}
}
}
stream << "\""
<< close_span();
}
void visit(const Variable *op) override {
stream << var(op->name);
}
void visit(const Cast *op) override {
stream << open_span("Cast");
stream << open_span("Matched");
stream << open_span("Type") << op->type << close_span();
stream << "(";
stream << close_span();
print(op->value);
stream << matched(")");
stream << close_span();
}
void visit(const Reinterpret *op) override {
stream << open_span("Reinterpret");
stream << open_span("Matched");
stream << open_span("Type") << op->type << close_span();
stream << "(";
stream << close_span();
print(op->value);
stream << matched(")");
stream << close_span();
}
void visit_binary_op(const Expr &a, const Expr &b, const char *op) {
stream << open_span("BinaryOp");
stream << matched("(");
print(a);
stream << " " << matched("Operator", op) << " ";
print(b);
stream << matched(")");
stream << close_span();
}
void visit(const Add *op) override {
visit_binary_op(op->a, op->b, "+");
}
void visit(const Sub *op) override {
visit_binary_op(op->a, op->b, "-");
}
void visit(const Mul *op) override {
visit_binary_op(op->a, op->b, "*");
}
void visit(const Div *op) override {
visit_binary_op(op->a, op->b, "/");
}
void visit(const Mod *op) override {
visit_binary_op(op->a, op->b, "%");
}
void visit(const And *op) override {
visit_binary_op(op->a, op->b, "&&");
}
void visit(const Or *op) override {
visit_binary_op(op->a, op->b, "||");
}
void visit(const NE *op) override {
visit_binary_op(op->a, op->b, "!=");
}
void visit(const LT *op) override {
visit_binary_op(op->a, op->b, "<");
}
void visit(const LE *op) override {
visit_binary_op(op->a, op->b, "<=");
}
void visit(const GT *op) override {
visit_binary_op(op->a, op->b, ">");
}
void visit(const GE *op) override {
visit_binary_op(op->a, op->b, ">=");
}
void visit(const EQ *op) override {
visit_binary_op(op->a, op->b, "==");
}
void visit(const Min *op) override {
stream << open_span("Min");
print_list(symbol("min") + "(", {op->a, op->b}, ")");
stream << close_span();
}
void visit(const Max *op) override {
stream << open_span("Max");
print_list(symbol("max") + "(", {op->a, op->b}, ")");
stream << close_span();
}
void visit(const Not *op) override {
stream << open_span("Not");
stream << "!";
print(op->a);
stream << close_span();
}
void visit(const Select *op) override {
stream << open_span("Select");
print_list(symbol("select") + "(", {op->condition, op->true_value, op->false_value}, ")");
stream << close_span();
}
void visit(const Load *op) override {
stream << open_span("Load");
stream << open_span("Matched");
stream << var(op->name) << "[";
stream << close_span();
print(op->index);
stream << matched("]");
if (!is_const_one(op->predicate)) {
stream << " " << keyword("if") << " ";
print(op->predicate);
}
stream << close_span();
}
void visit(const Ramp *op) override {
stream << open_span("Ramp");
print_list(symbol("ramp") + "(", {op->base, op->stride, Expr(op->lanes)}, ")");
stream << close_span();
}
void visit(const Broadcast *op) override {
stream << open_span("Broadcast");
stream << open_span("Matched");
stream << symbol("x") << op->lanes << "(";
stream << close_span();
print(op->value);
stream << matched(")");
stream << close_span();
}
void visit(const Call *op) override {
stream << open_span("Call");
print_list(symbol(op->name) + "(", op->args, ")");
stream << close_span();
}
void visit(const Let *op) override {
scope.push(op->name, unique_id());
stream << open_span("Let");
stream << open_span("Matched");
stream << "(" << keyword("let") << " ";
stream << var(op->name);
stream << close_span();
stream << " " << matched("Operator Assign", "=") << " ";
print(op->value);
stream << " " << matched("Keyword", "in") << " ";
print(op->body);
stream << matched(")");
stream << close_span();
scope.pop(op->name);
}
void visit(const LetStmt *op) override {
scope.push(op->name, unique_id());
stream << open_div("LetStmt") << open_line();
stream << open_span("Matched");
stream << keyword("let") << " ";
stream << var(op->name);
stream << close_span();
stream << " " << matched("Operator Assign", "=") << " ";
print(op->value);
stream << close_line();
print(op->body);
stream << close_div();
scope.pop(op->name);
}
void visit(const AssertStmt *op) override {
stream << open_div("AssertStmt WrapLine");
std::vector<Expr> args;
args.push_back(op->condition);
args.push_back(op->message);
print_list(symbol("assert") + "(", args, ")");
stream << close_div();
}
void visit(const ProducerConsumer *op) override {
scope.push(op->name, unique_id());
stream << open_div(op->is_producer ? "Produce" : "Consumer");
int produce_id = unique_id();
stream << open_span("Matched");
stream << open_expand_button(produce_id);
stream << keyword(op->is_producer ? "produce" : "consume") << " ";
stream << var(op->name);
stream << close_expand_button() << " {";
stream << close_span();
stream << open_div(op->is_producer ? "ProduceBody Indent" : "ConsumeBody Indent", produce_id);
print(op->body);
stream << close_div();
stream << matched("}");
stream << close_div();
scope.pop(op->name);
}
void visit(const For *op) override {
scope.push(op->name, unique_id());
stream << open_div("For");
int id = unique_id();
stream << open_expand_button(id);
stream << open_span("Matched");
if (op->for_type == ForType::Serial) {
stream << keyword("for");
} else if (op->for_type == ForType::Parallel) {
stream << keyword("parallel");
} else if (op->for_type == ForType::Vectorized) {
stream << keyword("vectorized");
} else if (op->for_type == ForType::Unrolled) {
stream << keyword("unrolled");
} else if (op->for_type == ForType::GPUBlock) {
stream << keyword("gpu_block");
} else if (op->for_type == ForType::GPUThread) {
stream << keyword("gpu_thread");
} else if (op->for_type == ForType::GPULane) {
stream << keyword("gpu_lane");
} else {
internal_error << "Unknown for type: " << ((int)op->for_type) << "\n";
}
stream << " (";
stream << close_span();
print_list({Variable::make(Int(32), op->name), op->min, op->extent});
stream << matched(")");
stream << close_expand_button();
stream << " " << matched("{");
stream << open_div("ForBody Indent", id);
print(op->body);
stream << close_div();
stream << matched("}");
stream << close_div();
scope.pop(op->name);
}
void visit(const Acquire *op) override {
stream << open_div("Acquire");
int id = unique_id();
stream << open_span("Matched");
stream << open_expand_button(id);
stream << keyword("acquire (");
stream << close_span();
print(op->semaphore);
stream << ", ";
print(op->count);
stream << matched(")");
stream << close_expand_button() << " {";
stream << open_div("Acquire Indent", id);
print(op->body);
stream << close_div();
stream << matched("}");
stream << close_div();
}
void visit(const Store *op) override {
stream << open_div("Store WrapLine");
stream << open_span("Matched");
stream << var(op->name) << "[";
stream << close_span();
print(op->index);
stream << matched("]");
stream << " " << span("Operator Assign Matched", "=") << " ";
stream << open_span("StoreValue");
print(op->value);
if (!is_const_one(op->predicate)) {
stream << " " << keyword("if") << " ";
print(op->predicate);
}
stream << close_span();
stream << close_div();
}
void visit(const Provide *op) override {
stream << open_div("Provide WrapLine");
stream << open_span("Matched");
stream << var(op->name) << "(";
stream << close_span();
print_list(op->args);
stream << matched(")") << " ";
stream << matched("=") << " ";
if (op->values.size() > 1) {
print_list("{", op->values, "}");
} else {
print(op->values[0]);
}
stream << close_div();
}
void visit(const Allocate *op) override {
scope.push(op->name, unique_id());
stream << open_div("Allocate");
stream << open_span("Matched");
stream << keyword("allocate") << " ";
stream << var(op->name) << "[";
stream << close_span();
stream << open_span("Type");
stream << op->type;
stream << close_span();
for (const auto &extent : op->extents) {
stream << " * ";
print(extent);
}
stream << matched("]");
if (!is_const_one(op->condition)) {
stream << " " << keyword("if") << " ";
print(op->condition);
}
if (op->new_expr.defined()) {
stream << open_span("Matched");
stream << keyword("custom_new") << "{";
print(op->new_expr);
stream << matched("}");
}
if (!op->free_function.empty()) {
stream << open_span("Matched");
stream << keyword("custom_delete") << "{ " << op->free_function << "(); ";
stream << matched("}");
}
stream << open_div("AllocateBody");
print(op->body);
stream << close_div();
stream << close_div();
scope.pop(op->name);
}
void visit(const Free *op) override {
stream << open_div("Free WrapLine");
stream << keyword("free") << " ";
stream << var(op->name);
stream << close_div();
}
void visit(const Realize *op) override {
scope.push(op->name, unique_id());
stream << open_div("Realize");
int id = unique_id();
stream << open_expand_button(id);
stream << keyword("realize") << " ";
stream << var(op->name);
stream << matched("(");
for (size_t i = 0; i < op->bounds.size(); i++) {
print_list("[", {op->bounds[i].min, op->bounds[i].extent}, "]");
if (i < op->bounds.size() - 1) {
stream << ", ";
}
}
stream << matched(")");
if (!is_const_one(op->condition)) {
stream << " " << keyword("if") << " ";
print(op->condition);
}
stream << close_expand_button();
stream << " " << matched("{");
stream << open_div("RealizeBody Indent", id);
print(op->body);
stream << close_div();
stream << matched("}");
stream << close_div();
scope.pop(op->name);
}
void visit(const Prefetch *op) override {
stream << open_span("Prefetch");
stream << keyword("prefetch") << " ";
stream << var(op->name);
stream << matched("(");
for (size_t i = 0; i < op->bounds.size(); i++) {
print_list("[", {op->bounds[i].min, op->bounds[i].extent}, "]");
if (i < op->bounds.size() - 1) {
stream << ", ";
}
}
stream << matched(")");
if (!is_const_one(op->condition)) {
stream << " " << keyword("if") << " ";
print(op->condition);
}
stream << close_span();
stream << open_div("PrefetchBody");
print(op->body);
stream << close_div();
}
// To avoid generating ridiculously deep DOMs, we flatten blocks here.
void visit_block_stmt(const Stmt &stmt) {
if (const Block *b = stmt.as<Block>()) {
visit_block_stmt(b->first);
visit_block_stmt(b->rest);
} else if (stmt.defined()) {
print(stmt);
}
}
void visit(const Block *op) override {
stream << open_div("Block");
visit_block_stmt(op->first);
visit_block_stmt(op->rest);
stream << close_div();
}
// We also flatten forks
void visit_fork_stmt(const Stmt &stmt) {
if (const Fork *f = stmt.as<Fork>()) {
visit_fork_stmt(f->first);
visit_fork_stmt(f->rest);
} else if (stmt.defined()) {
stream << open_div("ForkTask");
int id = unique_id();
stream << open_expand_button(id);
stream << matched("task {");
stream << close_expand_button();
stream << open_div("ForkTask Indent", id);
print(stmt);
stream << close_div();
stream << matched("}");
stream << close_div();
}
}
void visit(const Fork *op) override {
stream << open_div("Fork");
int id = unique_id();
stream << open_expand_button(id);
stream << keyword("fork") << " " << matched("{");
stream << close_expand_button();
stream << open_div("Fork Indent", id);
visit_fork_stmt(op->first);
visit_fork_stmt(op->rest);
stream << close_div();
stream << matched("}");
stream << close_div();
}
void visit(const IfThenElse *op) override {
stream << open_div("IfThenElse");
int id = unique_id();
stream << open_expand_button(id);
stream << open_span("Matched");
stream << keyword("if") << " (";
stream << close_span();
while (true) {
print(op->condition);
stream << matched(")");
stream << close_expand_button() << " ";
stream << matched("{"); // close if (or else if) span
stream << open_div("ThenBody Indent", id);
print(op->then_case);
stream << close_div(); // close thenbody div
if (!op->else_case.defined()) {
stream << matched("}");
break;
}
id = unique_id();
if (const IfThenElse *nested_if = op->else_case.as<IfThenElse>()) {
stream << matched("}") << " ";
stream << open_expand_button(id);
stream << open_span("Matched");
stream << keyword("else if") << " (";
stream << close_span();
op = nested_if;
} else {
stream << open_span("Matched") << "} ";
stream << open_expand_button(id);
stream << keyword("else");
stream << close_expand_button() << "{";
stream << close_span();
stream << open_div("ElseBody Indent", id);
print(op->else_case);
stream << close_div() << matched("}");
break;
}
}
stream << close_div(); // Closing ifthenelse div.
}
void visit(const Evaluate *op) override {
stream << open_div("Evaluate");
print(op->value);
stream << close_div();
}
void visit(const Shuffle *op) override {
stream << open_span("Shuffle");
if (op->is_concat()) {
print_list(symbol("concat_vectors("), op->vectors, ")");
} else if (op->is_interleave()) {
print_list(symbol("interleave_vectors("), op->vectors, ")");
} else if (op->is_extract_element()) {
std::vector<Expr> args = op->vectors;
args.emplace_back(op->slice_begin());
print_list(symbol("extract_element("), args, ")");
} else if (op->is_slice()) {
std::vector<Expr> args = op->vectors;
args.emplace_back(op->slice_begin());
args.emplace_back(op->slice_stride());
args.emplace_back(static_cast<int>(op->indices.size()));
print_list(symbol("slice_vectors("), args, ")");
} else {
std::vector<Expr> args = op->vectors;
for (int i : op->indices) {
args.emplace_back(i);
}
print_list(symbol("shuffle("), args, ")");
}
stream << close_span();
}
void visit(const VectorReduce *op) override {
stream << open_span("VectorReduce");
stream << open_span("Type") << op->type << close_span();
print_list(symbol("vector_reduce") + "(", {op->op, op->value}, ")");
stream << close_span();
}
void visit(const Atomic *op) override {
stream << open_div("Atomic");
int id = unique_id();
stream << open_expand_button(id);
stream << open_span("Matched");
if (op->mutex_name.empty()) {
stream << keyword("atomic") << matched("{");
} else {
stream << keyword("atomic") << " (";
stream << symbol(op->mutex_name);
stream << ")" << matched("{");
}
stream << close_span();
stream << open_div("Atomic Body Indent", id);
print(op->body);
stream << close_div() << matched("}");
stream << close_div();
}
public:
void print(const Expr &ir) {
ir.accept(this);
}
void print(const Stmt &ir) {
ir.accept(this);
}
void print(const LoweredFunc &op) {
scope.push(op.name, unique_id());
stream << open_div("Function");
int id = unique_id();
stream << open_expand_button(id);
stream << open_span("Matched");
stream << keyword("func");
stream << " " << op.name << "(";
stream << close_span();
for (size_t i = 0; i < op.args.size(); i++) {
if (i > 0) {
stream << matched(",") << " ";
}
stream << var(op.args[i].name);
}
stream << matched(")");
stream << close_expand_button();
stream << " " << matched("{");
stream << open_div("FunctionBody Indent", id);
print(op.body);
stream << close_div();
stream << matched("}");
stream << close_div();
scope.pop(op.name);
}
void print_cuda_gpu_source_kernels(const std::string &str) {
std::istringstream ss(str);
int current_id = -1;
stream << "<code class='ptx'>";
bool in_braces = false;
bool in_func_signature = false;
std::string current_kernel;
for (std::string line; std::getline(ss, line);) {
if (line.empty()) {
stream << "\n";
continue;
}
line = replace_all(line, "&", "&");
line = replace_all(line, "<", "<");
line = replace_all(line, ">", ">");
line = replace_all(line, "\"", """);
line = replace_all(line, "/", "/");
line = replace_all(line, "'", "'");
if (starts_with(line, ".visible .entry")) {
std::vector<std::string> parts = split_string(line, " ");
if (parts.size() == 3) {
in_func_signature = true;
current_id = unique_id();
stream << open_expand_button(current_id);
std::string kernel_name = parts[2].substr(0, parts[2].length() - 1);
line = keyword(".visible") + " " + keyword(".entry") + " ";
line += var(kernel_name) + " " + matched("(");
current_kernel = kernel_name;
}
} else if (starts_with(line, ")") && in_func_signature) {
stream << close_expand_button();
in_func_signature = false;
line = matched(")") + line.substr(1);
} else if (starts_with(line, "{") && !in_braces) {
in_braces = true;
stream << matched("{");
stream << close_expand_button();
internal_assert(current_id != -1);
stream << open_div("Indent", current_id);
current_id = -1;
line = line.substr(1);
scope.push(current_kernel, unique_id());
} else if (starts_with(line, "}") && in_braces) {
stream << close_div();
line = matched("}") + line.substr(1);
in_braces = false;
scope.pop(current_kernel);
}
bool indent = false;
if (line[0] == '\t') {
// Replace first tab with four spaces.
line = line.substr(1);
indent = true;
}
line = replace_all(line, ".f32", ".<span class='OpF32'>f32</span>");
line = replace_all(line, ".f64", ".<span class='OpF64'>f64</span>");
line = replace_all(line, ".s8", ".<span class='OpI8'>s8</span>");
line = replace_all(line, ".s16", ".<span class='OpI16'>s16</span>");
line = replace_all(line, ".s32", ".<span class='OpI32'>s32</span>");
line = replace_all(line, ".s64", ".<span class='OpI64'>s64</span>");
line = replace_all(line, ".u8", ".<span class='OpI8'>u8</span>");
line = replace_all(line, ".u16", ".<span class='OpI16'>u16</span>");
line = replace_all(line, ".u32", ".<span class='OpI32'>u32</span>");
line = replace_all(line, ".u64", ".<span class='OpI64'>u64</span>");
line = replace_all(line, ".b8", ".<span class='OpB8'>b8</span>");
line = replace_all(line, ".b16", ".<span class='OpB16'>b16</span>");
line = replace_all(line, ".b32", ".<span class='OpB32'>b32</span>");
line = replace_all(line, ".b64", ".<span class='OpB64'>b64</span>");
line = replace_all(line, ".v2", ".<span class='OpVec2'>v2</span>");
line = replace_all(line, ".v4", ".<span class='OpVec4'>v4</span>");
line = replace_all(line, "ld.", "<span class='Memory'>ld</span>.");
line = replace_all(line, "st.", "<span class='Memory'>st</span>.");
size_t idx;
if ((idx = line.find("//")) != std::string::npos) {
line.insert(idx, "<span class='Comment'>");
line += "</span>";
}
// Predicated instructions
if (line.front() == '@' && indent) {
idx = line.find(' ');
std::string pred = line.substr(1, idx - 1);
line = "<span class='Pred'>@" + var(pred) + "</span>" + line.substr(idx);
}
// Labels
if (line.front() == 'L' && !indent && (idx = line.find(':')) != std::string::npos) {
std::string label = line.substr(0, idx);
line = "<span class='Label'>" + var(label) + "</span>:" + line.substr(idx + 1);
}
// Highlight operands
if ((idx = line.find(" \t")) != std::string::npos && line.back() == ';') {
std::string operands_str = line.substr(idx + 2);
operands_str = operands_str.substr(0, operands_str.length() - 1);
std::vector<std::string> operands = split_string(operands_str, ", ");
operands_str = "";
for (size_t opidx = 0; opidx < operands.size(); ++opidx) {
std::string op = operands[opidx];
internal_assert(!op.empty());
if (opidx != 0) {
operands_str += ", ";
}
if (op.back() == '}') {
std::string reg = op.substr(0, op.size() - 1);
operands_str += var(reg) + '}';
} else if (op.front() == '%') {
operands_str += var(op);
} else if (op.find_first_not_of("-0123456789") == string::npos) {
operands_str += open_span("IntImm Imm");
operands_str += op;
operands_str += close_span();
} else if (starts_with(op, "0f") &&
op.find_first_not_of("0123456789ABCDEF", 2) == string::npos) {
operands_str += open_span("FloatImm Imm");
operands_str += op;
operands_str += close_span();
} else if (op.front() == '[' && op.back() == ']') {
size_t idx = op.find('+');
if (idx == std::string::npos) {
std::string reg = op.substr(1, op.size() - 2);
operands_str += '[' + var(reg) + ']';
} else {
std::string reg = op.substr(1, idx - 1);
std::string offset = op.substr(idx + 1);
offset = offset.substr(0, offset.size() - 1);
operands_str += '[' + var(reg) + "+";
operands_str += open_span("IntImm Imm");
operands_str += offset;
operands_str += close_span();
operands_str += ']';
}
} else if (op.front() == '{') {
std::string reg = op.substr(1);
operands_str += '{' + var(reg);
} else if (op.front() == 'L') {
// Labels
operands_str += "<span class='Label'>" + var(op) + "</span>";
} else {
operands_str += op;
}
}
operands_str += ";";
line = line.substr(0, idx + 2) + operands_str;
}
if (indent) {
stream << " ";
}
stream << line << "\n";
}
stream << "</code>";
}
void print(const Buffer<> &op) {
bool include_data = ends_with(op.name(), "_gpu_source_kernels");
int id = 0;
if (include_data) {
id = unique_id();
stream << open_expand_button(id);
}
stream << open_div("Buffer<>");
stream << keyword("buffer ") << var(op.name());
if (include_data) {
stream << " = ";
stream << matched("{");
stream << close_expand_button();
stream << open_div("BufferData Indent", id);
std::string str((const char *)op.data(), op.size_in_bytes());
if (starts_with(op.name(), "cuda_")) {
print_cuda_gpu_source_kernels(str);
} else {
stream << "<pre>\n";
stream << str;
stream << "</pre>\n";
}
stream << close_div();
stream << " " << matched("}");
}
stream << close_div();
}
void print(const Module &m) {
scope.push(m.name(), unique_id());
for (const auto &s : m.submodules()) {
print(s);
}
int id = unique_id();
stream << open_expand_button(id);
stream << open_div("Module");
stream << open_span("Matched");
stream << keyword("module") << " name=" << m.name() << ", target=" << m.target().to_string();
stream << close_span();
stream << close_expand_button();
stream << " " << matched("{");
stream << open_div("ModuleBody Indent", id);
for (const auto &b : m.buffers()) {
print(b);
}
for (const auto &f : m.functions()) {
print(f);
}
stream << close_div();
stream << matched("}");
stream << close_div();
scope.pop(m.name());
}
StmtToHtml(const string &filename)
: id_count(0), context_stack(1, 0) {
stream.open(filename.c_str());
stream << "<head>";
stream << "<style type='text/css'>" << css << "</style>\n";
stream << "<script language='javascript' type='text/javascript'>" + js + "</script>\n";
stream << "<link href='http://maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css' rel='stylesheet'>\n";
stream << "<script src='http://code.jquery.com/jquery-1.10.2.js'></script>\n";
stream << "</head>\n <body>\n";
}
~StmtToHtml() override {
stream << "<script>\n"
<< "$( '.Matched' ).each( function() {\n"
<< " this.onmouseover = function() { $('.Matched[id^=' + this.id.split('-')[0] + '-]').addClass('Highlight'); }\n"
<< " this.onmouseout = function() { $('.Matched[id^=' + this.id.split('-')[0] + '-]').removeClass('Highlight'); }\n"
<< "} );\n"
<< "</script>\n";
stream << "</body>";
}
};
const std::string StmtToHtml::css = "\n \
body { font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 12px; background: #f8f8f8; margin-left:15px; } \n \
a, a:hover, a:visited, a:active { color: inherit; text-decoration: none; } \n \
b { font-weight: normal; }\n \
p.WrapLine { margin: 0px; margin-left: 30px; text-indent:-30px; } \n \
div.WrapLine { margin-left: 30px; text-indent:-30px; } \n \
div.Indent { padding-left: 15px; }\n \
div.ShowHide { position:absolute; left:-12px; width:12px; height:12px; } \n \
span.Comment { color: #998; font-style: italic; }\n \
span.Keyword { color: #333; font-weight: bold; }\n \
span.Assign { color: #d14; font-weight: bold; }\n \
span.Symbol { color: #990073; }\n \
span.Type { color: #445588; font-weight: bold; }\n \
span.StringImm { color: #d14; }\n \
span.IntImm { color: #099; }\n \
span.FloatImm { color: #099; }\n \
b.Highlight { font-weight: bold; background-color: #DDD; }\n \
span.Highlight { font-weight: bold; background-color: #FF0; }\n \
span.OpF32 { color: hsl(106deg 100% 40%); font-weight: bold; }\n \
span.OpF64 { color: hsl(106deg 100% 30%); font-weight: bold; }\n \
span.OpB8 { color: hsl(208deg 100% 80%); font-weight: bold; }\n \
span.OpB16 { color: hsl(208deg 100% 70%); font-weight: bold; }\n \
span.OpB32 { color: hsl(208deg 100% 60%); font-weight: bold; }\n \
span.OpB64 { color: hsl(208deg 100% 50%); font-weight: bold; }\n \
span.OpI8 { color: hsl( 46deg 100% 45%); font-weight: bold; }\n \
span.OpI16 { color: hsl( 46deg 100% 40%); font-weight: bold; }\n \
span.OpI32 { color: hsl( 46deg 100% 34%); font-weight: bold; }\n \
span.OpI64 { color: hsl( 46deg 100% 27%); font-weight: bold; }\n \
span.OpVec2 { background-color: hsl(100deg 100% 90%); font-weight: bold; }\n \
span.OpVec4 { background-color: hsl(100deg 100% 80%); font-weight: bold; }\n \
span.Memory { color: #d22; font-weight: bold; }\n \
span.Pred { background-color: #ffe8bd; font-weight: bold; }\n \
span.Label { background-color: #bde4ff; font-weight: bold; }\n \
code.ptx { tab-size: 26; white-space: pre; }\n \
";
const std::string StmtToHtml::js = "\n \
function toggle(id) { \n \
e = document.getElementById(id); \n \
show = document.getElementById(id + '-show'); \n \
hide = document.getElementById(id + '-hide'); \n \
if (e.style.display != 'none') { \n \
e.style.display = 'none'; \n \
show.style.display = 'block'; \n \
hide.style.display = 'none'; \n \
} else { \n \
e.style.display = 'block'; \n \
show.style.display = 'none'; \n \
hide.style.display = 'block'; \n \
} \n \
return false; \n \
}";
} // namespace
void print_to_html(const string &filename, const Stmt &s) {
StmtToHtml sth(filename);
sth.print(s);
}
void print_to_html(const string &filename, const Module &m) {
StmtToHtml sth(filename);
sth.print(m);
}
} // namespace Internal
} // namespace Halide