#include "dc.h" #include "mem.h" #include "op.h" #include "str.h" #include #include static void indent(tobi_sb *sb, unsigned n) { for (unsigned i = 0; i < n; i++) { tobi_sb_add(sb, " "); } } static void expr(tobi_sb *sb, const tobi_ir *n); static void stmt(tobi_sb *sb, const tobi_ir *n, unsigned ind); static const char *binop(const char *name) { if (!name) { return NULL; } if (strcmp(name, "eq") == 0) return "=="; if (strcmp(name, "gt") == 0) return ">"; if (strcmp(name, "lt") == 0) return "<"; if (strcmp(name, "add") == 0) return "+"; if (strcmp(name, "sub") == 0) return "-"; if (strcmp(name, "mul") == 0) return "*"; if (strcmp(name, "div") == 0) return "/"; if (strcmp(name, "mod") == 0) return "%"; if (strcmp(name, "shl") == 0) return "<<"; if (strcmp(name, "shr") == 0) return ">>"; if (strcmp(name, "and") == 0) return "&"; if (strcmp(name, "or") == 0) return "|"; if (strcmp(name, "xor") == 0) return "^"; if (strcmp(name, "land") == 0) return "&&"; if (strcmp(name, "lor") == 0) return "||"; return NULL; } static const char *unop(const char *name) { if (!name) { return NULL; } if (strcmp(name, "lnot") == 0) return "!"; if (strcmp(name, "not") == 0) return "~"; return NULL; } static int op_prec(const char *name) { if (!name) { return 0; } if (strcmp(name, "lor") == 0) return 1; if (strcmp(name, "land") == 0) return 2; if (strcmp(name, "or") == 0) return 3; if (strcmp(name, "xor") == 0) return 4; if (strcmp(name, "and") == 0) return 5; if (strcmp(name, "eq") == 0 || strcmp(name, "gt") == 0 || strcmp(name, "lt") == 0) return 6; if (strcmp(name, "shl") == 0 || strcmp(name, "shr") == 0) return 7; if (strcmp(name, "add") == 0 || strcmp(name, "sub") == 0) return 8; if (strcmp(name, "mul") == 0 || strcmp(name, "div") == 0 || strcmp(name, "mod") == 0) return 9; if (unop(name)) return 10; return 0; } static int op_right_needs_group(const char *name) { if (!name) { return 1; } return !(strcmp(name, "add") == 0 || strcmp(name, "mul") == 0 || strcmp(name, "and") == 0 || strcmp(name, "or") == 0 || strcmp(name, "xor") == 0 || strcmp(name, "land") == 0 || strcmp(name, "lor") == 0); } static void expr_prec(tobi_sb *sb, const tobi_ir *n, int parent_prec) { if (!n) { tobi_sb_add(sb, ""); return; } switch (n->kind) { case TOBI_IR_INTEGER: tobi_sb_printf(sb, "0x%llx", (unsigned long long)n->value); return; case TOBI_IR_STRING: tobi_sb_ch(sb, '"'); if (n->str) { for (const char *p = n->str; *p; p++) { if (*p == '"' || *p == '\\') { tobi_sb_ch(sb, '\\'); } tobi_sb_ch(sb, *p); } } tobi_sb_ch(sb, '"'); return; case TOBI_IR_REF: tobi_sb_add(sb, n->name ? n->name : ""); return; case TOBI_IR_CALL: tobi_sb_add(sb, n->name ? n->name : ""); tobi_sb_ch(sb, '('); for (size_t i = 0; i < n->child_len; i++) { if (i) { tobi_sb_add(sb, ", "); } expr_prec(sb, n->child[i], 0); } tobi_sb_ch(sb, ')'); return; case TOBI_IR_EXPR: { const char *op = binop(n->name); int prec = op_prec(n->name); const tobi_op_expr *info = tobi_op_expr_find_name(n->name); size_t arg_len = n->child_len; if (info && info->target_count <= arg_len) { arg_len -= info->target_count; } if (op && n->child_len >= 2 && prec > 0) { int paren = prec < parent_prec; if (paren) { tobi_sb_ch(sb, '('); } expr_prec(sb, n->child[0], prec); tobi_sb_printf(sb, " %s ", op); expr_prec(sb, n->child[1], prec + op_right_needs_group(n->name)); if (paren) { tobi_sb_ch(sb, ')'); } return; } op = unop(n->name); if (op && arg_len == 1 && prec > 0) { int paren = prec < parent_prec; if (paren) { tobi_sb_ch(sb, '('); } tobi_sb_add(sb, op); expr_prec(sb, n->child[0], prec); if (paren) { tobi_sb_ch(sb, ')'); } return; } tobi_sb_printf(sb, "%s(", n->name ? n->name : "op"); for (size_t i = 0; i < arg_len; i++) { if (i) { tobi_sb_add(sb, ", "); } expr_prec(sb, n->child[i], 0); } tobi_sb_ch(sb, ')'); return; } case TOBI_IR_BUFFER: tobi_sb_printf(sb, "Buffer[%s]", n->str ? n->str : ""); return; case TOBI_IR_PACKAGE: tobi_sb_ch(sb, '{'); for (size_t i = 0; i < n->child_len; i++) { if (i) { tobi_sb_add(sb, ", "); } expr_prec(sb, n->child[i], 0); } tobi_sb_ch(sb, '}'); return; case TOBI_IR_STORE: if (n->child_len >= 2) { expr_prec(sb, n->child[1], 0); tobi_sb_add(sb, " = "); expr_prec(sb, n->child[0], 0); } else { tobi_sb_add(sb, "store(?)"); } return; case TOBI_IR_UNKNOWN: tobi_sb_printf(sb, "unknown_0x%x", n->raw_op); return; default: tobi_sb_add(sb, n->name ? n->name : tobi_ir_kind_name(n->kind)); return; } } static void expr(tobi_sb *sb, const tobi_ir *n) { expr_prec(sb, n, 0); } static int expr_has_target(const tobi_ir *n) { if (!n || n->kind != TOBI_IR_EXPR || !n->name) { return 0; } const tobi_op_expr *info = tobi_op_expr_find_name(n->name); return info && info->target_count > 0 && n->child_len >= (size_t)info->arg_count + info->target_count; } static void expr_target_stmt(tobi_sb *sb, const tobi_ir *n) { const tobi_op_expr *info = tobi_op_expr_find_name(n->name); if (!info || info->target_count == 0 || n->child_len < (size_t)info->arg_count + info->target_count) { expr(sb, n); return; } if (info->target_count == 2 && strcmp(n->name, "div") == 0 && n->child_len >= 4) { expr(sb, n->child[2]); tobi_sb_add(sb, ", "); expr(sb, n->child[3]); tobi_sb_add(sb, " = divmod("); expr(sb, n->child[0]); tobi_sb_add(sb, ", "); expr(sb, n->child[1]); tobi_sb_ch(sb, ')'); return; } expr(sb, n->child[info->arg_count]); tobi_sb_add(sb, " = "); expr(sb, n); } static void block(tobi_sb *sb, const tobi_ir *b, unsigned ind) { if (!b) { return; } for (size_t i = 0; i < b->child_len; i++) { stmt(sb, b->child[i], ind); } } static void stmt(tobi_sb *sb, const tobi_ir *n, unsigned ind) { if (!n) { return; } switch (n->kind) { case TOBI_IR_SCOPE: indent(sb, ind); tobi_sb_printf(sb, "scope %s {\n", n->path ? n->path : n->name); block(sb, n->child_len ? n->child[0] : NULL, ind + 1); indent(sb, ind); tobi_sb_add(sb, "}\n"); break; case TOBI_IR_DEVICE: indent(sb, ind); tobi_sb_printf(sb, "device %s {\n", n->path ? n->path : n->name); block(sb, n->child_len ? n->child[0] : NULL, ind + 1); indent(sb, ind); tobi_sb_add(sb, "}\n"); break; case TOBI_IR_METHOD: indent(sb, ind); tobi_sb_printf(sb, "method %s(args=%u, serialized=%s, sync=%u) {\n", n->name ? n->name : "", n->method_args, n->method_serialized ? "true" : "false", n->method_sync); block(sb, n->child_len ? n->child[0] : NULL, ind + 1); indent(sb, ind); tobi_sb_add(sb, "}\n"); break; case TOBI_IR_NAME: indent(sb, ind); tobi_sb_printf(sb, "name %s = ", n->path ? n->path : n->name); if (n->child_len) { expr(sb, n->child[0]); } else { tobi_sb_add(sb, ""); } tobi_sb_add(sb, ";\n"); break; case TOBI_IR_OPREGION: indent(sb, ind); tobi_sb_printf(sb, "opregion %s space=%s(%llu) offset=", n->path ? n->path : n->name, n->str ? n->str : "unknown", (unsigned long long)n->value); expr(sb, n->child_len > 0 ? n->child[0] : NULL); tobi_sb_add(sb, " length="); expr(sb, n->child_len > 1 ? n->child[1] : NULL); tobi_sb_add(sb, ";\n"); break; case TOBI_IR_FIELD: indent(sb, ind); tobi_sb_printf(sb, "%s %s", n->str ? n->str : "field", n->name ? n->name : ""); if (n->target) { tobi_sb_printf(sb, " backing=%s", n->target); } tobi_sb_printf(sb, " flags=0x%llx access=%u lock=%u update=%u {\n", (unsigned long long)n->value, n->access_type, n->lock_rule, n->update_rule); for (size_t i = 0; i < n->child_len; i++) { indent(sb, ind + 1); if (n->child[i]->kind == TOBI_IR_FIELD_ELEM) { tobi_sb_printf(sb, "%s @%llu:%llu", n->child[i]->name ? n->child[i]->name : "", (unsigned long long)n->child[i]->bit_off, (unsigned long long)n->child[i]->bit_len); if (n->child[i]->str) { tobi_sb_printf(sb, " /* %s */", n->child[i]->str); } tobi_sb_add(sb, ";\n"); } else { tobi_sb_add(sb, "bank_value = "); expr(sb, n->child[i]); tobi_sb_add(sb, ";\n"); } } indent(sb, ind); tobi_sb_add(sb, "}\n"); break; case TOBI_IR_STORE: indent(sb, ind); expr(sb, n); tobi_sb_add(sb, ";\n"); break; case TOBI_IR_EXPR: case TOBI_IR_CALL: indent(sb, ind); if (expr_has_target(n)) { expr_target_stmt(sb, n); } else { expr(sb, n); } tobi_sb_add(sb, ";\n"); break; case TOBI_IR_RETURN: indent(sb, ind); tobi_sb_add(sb, "return"); if (n->child_len) { tobi_sb_ch(sb, ' '); expr(sb, n->child[0]); } tobi_sb_add(sb, ";\n"); break; case TOBI_IR_IF: indent(sb, ind); tobi_sb_add(sb, "if ("); expr(sb, n->child_len ? n->child[0] : NULL); tobi_sb_add(sb, ") {\n"); block(sb, n->child_len > 1 ? n->child[1] : NULL, ind + 1); indent(sb, ind); if (n->child_len > 2) { tobi_sb_add(sb, "} else {\n"); block(sb, n->child[2], ind + 1); indent(sb, ind); } tobi_sb_add(sb, "}\n"); break; case TOBI_IR_WHILE: indent(sb, ind); tobi_sb_add(sb, "while ("); expr(sb, n->child_len ? n->child[0] : NULL); tobi_sb_add(sb, ") {\n"); block(sb, n->child_len > 1 ? n->child[1] : NULL, ind + 1); indent(sb, ind); tobi_sb_add(sb, "}\n"); break; case TOBI_IR_BREAK: indent(sb, ind); tobi_sb_add(sb, "break;\n"); break; case TOBI_IR_CONTINUE: indent(sb, ind); tobi_sb_add(sb, "continue;\n"); break; case TOBI_IR_UNKNOWN: indent(sb, ind); tobi_sb_printf(sb, "/* unknown opcode 0x%x at 0x%zx", n->raw_op, n->off); if (n->str) { tobi_sb_printf(sb, ": %s", n->str); } tobi_sb_add(sb, " */\n"); break; case TOBI_IR_DIAG: indent(sb, ind); tobi_sb_printf(sb, "/* %s at 0x%zx */\n", n->str ? n->str : "diagnostic", n->off); break; case TOBI_IR_BLOCK: block(sb, n, ind); break; default: indent(sb, ind); tobi_sb_printf(sb, "/* %s at 0x%zx */\n", tobi_ir_kind_name(n->kind), n->off); break; } } char *tobi_dc_emit(const tobi_ir *root, const tobi_diag_list *diag) { tobi_sb sb; tobi_sb_init(&sb); if (diag) { for (size_t i = 0; i < diag->len; i++) { if (diag->items[i].level == TOBI_DIAG_WARN) { tobi_sb_printf(&sb, "/* warning at 0x%zx: %s */\n", diag->items[i].off, diag->items[i].msg); } } } if (root) { for (size_t i = 0; i < root->child_len; i++) { stmt(&sb, root->child[i], 0); } } return tobi_sb_take(&sb); }