diff --git a/src/jit/ir/ir.c b/src/jit/ir/ir.c index 36a67133..e6521554 100644 --- a/src/jit/ir/ir.c +++ b/src/jit/ir/ir.c @@ -8,6 +8,10 @@ const struct ir_opdef ir_opdefs[IR_NUM_OPS] = { #include "jit/ir/ir_ops.inc" }; +const char *ir_meta_names[IR_NUM_META] = { + "addr", "cycles", "fastmem", "reg", +}; + static void *ir_calloc(struct ir *ir, int size) { CHECK_LE(ir->used + size, ir->capacity); uint8_t *ptr = ir->buffer + ir->used; @@ -215,7 +219,7 @@ struct ir_value *ir_alloc_int(struct ir *ir, int64_t c, enum ir_type type) { v->i64 = c; break; default: - LOG_FATAL("Unexpected value type"); + LOG_FATAL("unexpected value type"); break; } v->reg = NO_REGISTER; @@ -358,11 +362,48 @@ uint64_t ir_zext_constant(const struct ir_value *v) { case VALUE_I64: return (uint64_t)v->i64; default: - LOG_FATAL("Unexpected value type"); + LOG_FATAL("unexpected value type"); break; } } +struct ir_value *ir_get_meta(struct ir *ir, const void *obj, int kind) { + struct list *bkt = hash_bkt(ir->meta[kind], obj); + + hash_bkt_for_each_entry(meta, bkt, struct ir_meta, it) { + if (meta->key == obj) { + CHECK(ir_is_constant(meta->value)); + return meta->value; + } + } + + return NULL; +} + +void ir_set_meta(struct ir *ir, const void *obj, int kind, + struct ir_value *value) { + struct list *bkt = hash_bkt(ir->meta[kind], obj); + struct ir_meta *meta = NULL; + + CHECK(ir_is_constant(value)); + + /* reuse existing meta object if possible */ + hash_bkt_for_each_entry(it, bkt, struct ir_meta, it) { + if (it->key == obj) { + meta = it; + break; + } + } + + if (!meta) { + meta = ir_calloc(ir, sizeof(struct ir_meta)); + meta->key = obj; + hash_add(bkt, &meta->it); + } + + meta->value = value; +} + void ir_source_info(struct ir *ir, uint32_t addr, int index) { struct ir_instr *instr = ir_append_instr(ir, OP_SOURCE_INFO, VALUE_V); ir_set_arg0(ir, instr, ir_alloc_i32(ir, addr)); diff --git a/src/jit/ir/ir.h b/src/jit/ir/ir.h index fac6b4ed..f9e2dbe7 100644 --- a/src/jit/ir/ir.h +++ b/src/jit/ir/ir.h @@ -3,6 +3,7 @@ #include #include "core/assert.h" +#include "core/hash.h" #include "core/list.h" #define IR_MAX_ARGS 4 @@ -45,6 +46,14 @@ enum ir_cmp { CMP_ULT }; +enum ir_meta_type { + IR_META_ADDR, + IR_META_CYCLES, + IR_META_FASTMEM, + IR_META_REG, + IR_NUM_META, +}; + struct ir_block; struct ir_instr; struct ir_value; @@ -95,6 +104,12 @@ struct ir_value { intptr_t tag; }; +struct ir_meta { + const void *key; + struct ir_value *value; + struct list_node it; +}; + struct ir_instr { enum ir_op op; @@ -117,7 +132,7 @@ struct ir_instr { struct list_node it; }; -/* blocks are collections of instructions, terminating in a single branch */ +/* control flow edge between blocks */ struct ir_edge { struct ir_block *src; struct ir_block *dst; @@ -126,6 +141,7 @@ struct ir_edge { struct list_node it; }; +/* blocks are collections of instructions, terminating in a single branch */ struct ir_block { struct list instrs; @@ -133,11 +149,10 @@ struct ir_block { struct list outgoing; struct list incoming; - /* intrusive iterator used by ir struct */ - struct list_node it; - /* generic meta data used by optimization passes */ intptr_t tag; + + struct list_node it; }; /* locals are allocated for values that need to be spilled to the stack @@ -153,21 +168,26 @@ struct ir_insert_point { }; struct ir { - /* backing memory buffer used by all ir allocations */ + /* backing memory buffer used by all allocations */ uint8_t *buffer; int capacity; int used; - /* total size of locals allocated */ - int locals_size; - /* current insert point */ struct ir_insert_point cursor; + /* current blocks in ir unit */ struct list blocks; + + /* total size of locals allocated */ + int locals_size; + + /* hashtables for each kind of meta data, keyed by each user's pointer */ + DECLARE_HASHTABLE(meta[IR_NUM_META], 7); }; extern const struct ir_opdef ir_opdefs[IR_NUM_OPS]; +extern const char *ir_meta_names[IR_NUM_META]; static inline int ir_type_size(enum ir_type type) { switch (type) { @@ -251,6 +271,11 @@ void ir_replace_uses(struct ir_value *v, struct ir_value *other); uint64_t ir_zext_constant(const struct ir_value *v); +/* attach meta data to instructions and blocks */ +struct ir_value *ir_get_meta(struct ir *ir, const void *obj, int kind); +void ir_set_meta(struct ir *ir, const void *obj, int kind, + struct ir_value *value); + /* provides information to map guest instructions to host instructions */ void ir_source_info(struct ir *ir, uint32_t addr, int index); diff --git a/src/jit/ir/ir_read.c b/src/jit/ir/ir_read.c index c9f16c86..c056cfc0 100644 --- a/src/jit/ir/ir_read.c +++ b/src/jit/ir/ir_read.c @@ -80,7 +80,7 @@ static void ir_lex_next(struct ir_parser *p) { } /* test for assignment operator */ - if (next == ':' || next == ',' || next == '=') { + if (next == ':' || next == ',' || next == '=' || next == '!') { snprintf(p->val.s, sizeof(p->val.s), "%c", next); p->tok = TOK_OPERATOR; return; @@ -307,6 +307,49 @@ static int ir_parse_label(struct ir_parser *p, int *label) { return 1; } +static int ir_parse_constant(struct ir_parser *p, enum ir_type type, + struct ir_value **value) { + if (p->tok != TOK_INTEGER) { + LOG_INFO("unexpected token %d when parsing constant", p->tok); + return 0; + } + + switch (type) { + case VALUE_I8: { + uint8_t v = (uint8_t)p->val.i; + *value = ir_alloc_i8(p->ir, v); + } break; + case VALUE_I16: { + uint16_t v = (uint16_t)p->val.i; + *value = ir_alloc_i16(p->ir, v); + } break; + case VALUE_I32: { + uint32_t v = (uint32_t)p->val.i; + *value = ir_alloc_i32(p->ir, v); + } break; + case VALUE_I64: { + uint64_t v = (uint64_t)p->val.i; + *value = ir_alloc_i64(p->ir, v); + } break; + case VALUE_F32: { + uint32_t v = (uint32_t)p->val.i; + *value = ir_alloc_f32(p->ir, *(float *)&v); + } break; + case VALUE_F64: { + uint64_t v = (uint64_t)p->val.i; + *value = ir_alloc_f64(p->ir, *(double *)&v); + } break; + default: + LOG_FATAL("unexpected value type"); + break; + } + + /* eat token */ + ir_lex_next(p); + + return 1; +} + static int ir_parse_arg(struct ir_parser *p, struct ir_instr *instr, int arg) { /* parse value type */ enum ir_type type; @@ -327,48 +370,62 @@ static int ir_parse_arg(struct ir_parser *p, struct ir_instr *instr, int arg) { been parsed */ int id = strtol(&ident[1], NULL, 10); ir_defer_reference(p, instr, arg, type, id); - } else if (p->tok == TOK_INTEGER || p->tok == TOK_STRING) { - struct ir_value *value = NULL; - switch (type) { - case VALUE_I8: { - uint8_t v = (uint8_t)p->val.i; - value = ir_alloc_i8(p->ir, v); - } break; - case VALUE_I16: { - uint16_t v = (uint16_t)p->val.i; - value = ir_alloc_i16(p->ir, v); - } break; - case VALUE_I32: { - uint32_t v = (uint32_t)p->val.i; - value = ir_alloc_i32(p->ir, v); - } break; - case VALUE_I64: { - uint64_t v = (uint64_t)p->val.i; - value = ir_alloc_i64(p->ir, v); - } break; - case VALUE_F32: { - uint32_t v = (uint32_t)p->val.i; - value = ir_alloc_f32(p->ir, *(float *)&v); - } break; - case VALUE_F64: { - uint64_t v = (uint64_t)p->val.i; - value = ir_alloc_f64(p->ir, *(double *)&v); - } break; - default: - LOG_FATAL("unexpected value type"); - break; + /* eat token */ + ir_lex_next(p); + } else { + struct ir_value *value = NULL; + if (!ir_parse_constant(p, type, &value)) { + return 0; } ir_set_arg(p->ir, instr, arg, value); - } else { - LOG_INFO("unexpected token %d when parsing value: %s", p->tok, p->val.s); - return 0; } - /* eat token */ + return 1; +} + +static int ir_parse_meta(struct ir_parser *p, void *obj) { + if (p->tok != TOK_OPERATOR || p->val.s[0] != '!') { + /* meta data is optional */ + return 1; + } ir_lex_next(p); + while (p->tok == TOK_IDENTIFIER) { + for (int kind = 0; kind < IR_NUM_META; kind++) { + if (strcasecmp(p->val.s, ir_meta_names[kind])) { + continue; + } + + /* eat name */ + ir_lex_next(p); + + /* parse value type */ + enum ir_type type; + if (!ir_parse_type(p, &type)) { + return 0; + } + + /* parse value */ + struct ir_value *value = NULL; + if (!ir_parse_constant(p, type, &value)) { + return 0; + } + + /* attach meta data to object */ + ir_set_meta(p->ir, obj, kind, value); + + /* break if no comma */ + if (p->tok != TOK_OPERATOR) { + break; + } + + /* eat comma and move onto the next argument */ + ir_lex_next(p); + } + } + return 1; } @@ -407,6 +464,7 @@ static int ir_parse_instr(struct ir_parser *p) { return 0; } + /* break if no comma */ if (p->tok != TOK_OPERATOR) { break; } @@ -416,6 +474,10 @@ static int ir_parse_instr(struct ir_parser *p) { } } + if (!ir_parse_meta(p, instr)) { + return 0; + } + ir_insert_instr_label(p, instr, p->next_label++); return 1; @@ -442,6 +504,10 @@ static int ir_parse_block(struct ir_parser *p) { ir_insert_block_label(p, block, p->next_label++); ir_set_current_block(p->ir, block); + if (!ir_parse_meta(p, block)) { + return 0; + } + return 1; } diff --git a/src/jit/ir/ir_write.c b/src/jit/ir/ir_write.c index 3839a317..30e90d82 100644 --- a/src/jit/ir/ir_write.c +++ b/src/jit/ir/ir_write.c @@ -115,6 +115,35 @@ static void ir_write_value(struct ir_writer *w, const struct ir_value *value, } } +static void ir_write_meta(struct ir_writer *w, const void *obj, FILE *output) { + int need_exclamation = 1; + int need_comma = 0; + + for (int kind = 0; kind < IR_NUM_META; kind++) { + struct ir_value *value = ir_get_meta(w->ir, obj, kind); + + if (!value) { + continue; + } + + if (need_exclamation) { + fprintf(output, " !"); + need_exclamation = 0; + } + + if (need_comma) { + fprintf(output, ", "); + need_comma = 0; + } + + fprintf(output, "%s ", ir_meta_names[kind]); + + ir_write_value(w, value, output); + + need_comma = 1; + } +} + static void ir_write_instr(struct ir_writer *w, const struct ir_instr *instr, FILE *output) { /* print result value if it exists */ @@ -125,9 +154,9 @@ static void ir_write_instr(struct ir_writer *w, const struct ir_instr *instr, /* print the actual op */ ir_write_op(w, instr->op, output); - fprintf(output, " "); /* print each argument */ + int need_space = 1; int need_comma = 0; for (int i = 0; i < 3; i++) { @@ -137,6 +166,11 @@ static void ir_write_instr(struct ir_writer *w, const struct ir_instr *instr, continue; } + if (need_space) { + fprintf(output, " "); + need_space = 0; + } + if (need_comma) { fprintf(output, ", "); need_comma = 0; @@ -147,6 +181,8 @@ static void ir_write_instr(struct ir_writer *w, const struct ir_instr *instr, need_comma = 1; } + ir_write_meta(w, instr, output); + #if 0 fprintf(output, "\t# tag=%" PRId64 " reg=%d", instr->tag, instr->result ? instr->result->reg : -1); @@ -157,7 +193,9 @@ static void ir_write_instr(struct ir_writer *w, const struct ir_instr *instr, static void ir_write_block(struct ir_writer *w, const struct ir_block *block, FILE *output) { - fprintf(output, "%%%d:\n", ir_get_block_label(w, block)); + fprintf(output, "%%%d:", ir_get_block_label(w, block)); + ir_write_meta(w, block, output); + fprintf(output, "\n"); list_for_each_entry(instr, &block->instrs, struct ir_instr, it) { ir_write_instr(w, instr, output);