Initial support for thread local storage + optimization of some related code selection patterns

This commit is contained in:
Dmitry Stogov 2022-06-22 16:02:43 +03:00
parent 9b25587eb6
commit a165c43196
5 changed files with 90 additions and 5 deletions

1
ir.c
View File

@ -136,6 +136,7 @@ void ir_print_const(ir_ctx *ctx, ir_insn *insn, FILE *f)
#define ir_op_flag_r IR_OP_FLAG_DATA // "d" and "r" are the same now
#define ir_op_flag_r0 ir_op_flag_r
#define ir_op_flag_r0X1 (ir_op_flag_r | 0 | (1 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_r0X3 (ir_op_flag_r | 0 | (3 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_r1 (ir_op_flag_r | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_r1X1 (ir_op_flag_r | 1 | (2 << IR_OP_FLAG_OPERANDS_SHIFT))
#define ir_op_flag_r1X2 (ir_op_flag_r | 1 | (3 << IR_OP_FLAG_OPERANDS_SHIFT))

1
ir.h
View File

@ -220,6 +220,7 @@ typedef enum _ir_type {
_(VAR, r1X1, reg, str, ___) /* local variable */ \
_(FUNC, r0, ___, ___, ___) /* constant func ref */ \
_(STR, r0, ___, ___, ___) /* constant str ref */ \
_(TLS, r0X3, num, num, num) /* thread local variable */ \
\
/* call ops */ \
_(CALL, xN, src, def, def) /* CALL(src, func, args...) */ \

View File

@ -171,6 +171,9 @@ static const int8_t _ir_fp_reg_params[IR_REG_FP_ARGS] = {
const char *ir_reg_name(int8_t reg, ir_type type)
{
IR_ASSERT(reg >= 0 && reg < IR_REG_NUM);
if (type == IR_VOID) {
type = (reg < IR_REG_FP_FIRST) ? IR_ADDR : IR_DOUBLE;
}
if (ir_type_size[type] == 8) {
return _ir_reg_name[reg];
} else {

View File

@ -362,7 +362,7 @@ void ir_dump_live_ranges(ir_ctx *ctx, FILE *f)
if (ival) {
ir_live_range *p = &ival->range;
fprintf(f, "[%%%s] : [%d.%d-%d.%d)",
ir_reg_name(ival->reg, IR_ADDR),
ir_reg_name(ival->reg, ival->type),
IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
p = p->next;

View File

@ -515,6 +515,9 @@ static const int8_t *_ir_fp_reg_params = NULL;
const char *ir_reg_name(int8_t reg, ir_type type)
{
IR_ASSERT(reg >= 0 && reg < IR_REG_NUM);
if (type == IR_VOID) {
type = (reg < IR_REG_FP_FIRST) ? IR_ADDR : IR_DOUBLE;
}
if (IR_IS_TYPE_FP(type) || ir_type_size[type] == 8) {
return _ir_reg_name[reg];
} else if (ir_type_size[type] == 4) {
@ -1193,12 +1196,21 @@ static uint32_t ir_match_insn(ir_ctx *ctx, ir_ref ref, ir_block *bb)
&& insn->op1 < ref
&& !ctx->rules[insn->op1]
&& ctx->ir_base[insn->op1].op == IR_LOAD) {
ir_insn *addr_insn = &ctx->ir_base[ctx->ir_base[insn->op1].op2];
ir_ref addr_ref = ctx->ir_base[insn->op1].op2;
ir_insn *addr_insn = &ctx->ir_base[addr_ref];
if (addr_insn->op == IR_RLOAD ||
(addr_insn->op == IR_C_ADDR &&
(sizeof(void*) == 4 || IR_IS_SIGNED_32BIT(addr_insn->val.i64)))) {
ctx->rules[insn->op1] = IR_SKIP_MEM;
} else {
if (!ctx->rules[addr_ref]) {
ctx->rules[addr_ref] = ir_match_insn(ctx, addr_ref, bb);
}
if (ctx->rules[addr_ref] == IR_LEA_OB || ctx->rules[addr_ref] == IR_SKIP_MEM) {
ctx->rules[insn->op1] = IR_SKIP_MEM;
ctx->rules[addr_ref] = IR_SKIP_MEM;
}
}
}
return IR_CMP_INT;
@ -1719,12 +1731,21 @@ store_int:
&& insn->op1 < ref
&& !ctx->rules[insn->op1]
&& ctx->ir_base[insn->op1].op == IR_LOAD) {
ir_insn *addr_insn = &ctx->ir_base[ctx->ir_base[insn->op1].op2];
ir_ref addr_ref = ctx->ir_base[insn->op1].op2;
ir_insn *addr_insn = &ctx->ir_base[addr_ref];
if (addr_insn->op == IR_RLOAD ||
(addr_insn->op == IR_C_ADDR &&
(sizeof(void*) == 4 || IR_IS_SIGNED_32BIT(addr_insn->val.i64)))) {
ctx->rules[insn->op1] = IR_SKIP_MEM;
} else {
if (!ctx->rules[addr_ref]) {
ctx->rules[addr_ref] = ir_match_insn(ctx, addr_ref, bb);
}
if (ctx->rules[addr_ref] == IR_LEA_OB || ctx->rules[addr_ref] == IR_SKIP_MEM) {
ctx->rules[insn->op1] = IR_SKIP_MEM;
ctx->rules[addr_ref] = IR_SKIP_MEM;
}
}
}
return IR_CMP_AND_BRANCH_INT;
@ -1752,12 +1773,21 @@ store_int:
&& insn->op1 < ref
&& !ctx->rules[insn->op1]
&& ctx->ir_base[insn->op1].op == IR_LOAD) {
ir_insn *addr_insn = &ctx->ir_base[ctx->ir_base[insn->op1].op2];
ir_ref addr_ref = ctx->ir_base[insn->op1].op2;
ir_insn *addr_insn = &ctx->ir_base[addr_ref];
if (addr_insn->op == IR_RLOAD ||
(addr_insn->op == IR_C_ADDR &&
(sizeof(void*) == 4 || IR_IS_SIGNED_32BIT(addr_insn->val.i64)))) {
ctx->rules[insn->op1] = IR_SKIP_MEM;
} else {
if (!ctx->rules[addr_ref]) {
ctx->rules[addr_ref] = ir_match_insn(ctx, addr_ref, bb);
}
if (ctx->rules[addr_ref] == IR_LEA_OB || ctx->rules[addr_ref] == IR_SKIP_MEM) {
ctx->rules[insn->op1] = IR_SKIP_MEM;
ctx->rules[addr_ref] = IR_SKIP_MEM;
}
}
}
return IR_GUARD_CMP_INT;
@ -1775,12 +1805,21 @@ store_int:
&& insn->op2 < ref
&& !ctx->rules[insn->op2]
&& ctx->ir_base[insn->op2].op == IR_LOAD) {
ir_insn *addr_insn = &ctx->ir_base[ctx->ir_base[insn->op2].op2];
ir_ref addr_ref = ctx->ir_base[insn->op2].op2;
ir_insn *addr_insn = &ctx->ir_base[addr_ref];
if (addr_insn->op == IR_RLOAD ||
(addr_insn->op == IR_C_ADDR &&
(sizeof(void*) == 4 || IR_IS_SIGNED_32BIT(addr_insn->val.i64)))) {
ctx->rules[insn->op2] = IR_SKIP_MEM;
} else {
if (!ctx->rules[addr_ref]) {
ctx->rules[addr_ref] = ir_match_insn(ctx, addr_ref, bb);
}
if (ctx->rules[addr_ref] == IR_LEA_OB || ctx->rules[addr_ref] == IR_SKIP_MEM) {
ctx->rules[insn->op2] = IR_SKIP_MEM;
ctx->rules[addr_ref] = IR_SKIP_MEM;
}
}
}
return insn->op;
@ -3252,6 +3291,10 @@ static void ir_emit_cmp_int_common(ir_ctx *ctx, ir_type type, ir_insn *insn, ir_
op1_reg = ctx->regs[insn->op1][2];
IR_ASSERT(op1_reg != IR_REG_NONE);
| ASM_MEM_REG_OP cmp, type, [Ra(op1_reg)], op2_reg
} else if (addr_insn->op == IR_ADD) {
op1_reg = ctx->regs[load_insn->op2][1];
IR_ASSERT(op1_reg != IR_REG_NONE && IR_IS_CONST_REF(addr_insn->op2));
| ASM_MEM_REG_OP cmp, type, [Ra(op1_reg)+ctx->ir_base[addr_insn->op2].val.i32], op2_reg
} else {
IR_ASSERT(0);
}
@ -3266,6 +3309,10 @@ static void ir_emit_cmp_int_common(ir_ctx *ctx, ir_type type, ir_insn *insn, ir_
op1_reg = ctx->regs[insn->op1][2];
IR_ASSERT(op1_reg != IR_REG_NONE);
| ASM_MEM_IMM_OP cmp, type, [Ra(op1_reg)], ctx->ir_base[op2].val.i32
} else if (addr_insn->op == IR_ADD) {
op1_reg = ctx->regs[load_insn->op2][1];
IR_ASSERT(op1_reg != IR_REG_NONE && IR_IS_CONST_REF(addr_insn->op2));
| ASM_MEM_IMM_OP cmp, type, [Ra(op1_reg)+ctx->ir_base[addr_insn->op2].val.i32], ctx->ir_base[op2].val.i32
} else {
IR_ASSERT(0);
}
@ -5783,6 +5830,36 @@ static void ir_emit_lea(ir_ctx *ctx, ir_ref def, ir_type type, ir_reg base_reg,
}
}
static void ir_emit_tls(ir_ctx *ctx, ir_ref def, ir_insn *insn)
{
ir_backend_data *data = ctx->data;
dasm_State **Dst = &data->dasm_state;
ir_reg reg = IR_REG_NUM(ctx->regs[def][0]);
|.if X64
| fs
|| if (insn->op1) {
| mov Ra(reg), aword [insn->op1]
|| } else {
| mov Ra(reg), [0x8]
| mov Ra(reg), aword [Ra(reg)+insn->op2]
| mov Ra(reg), aword [Ra(reg)+insn->op3]
|| }
|.else
| gs
|| if (insn->op1) {
| mov Ra(reg), aword [insn->op1]
|| } else {
| mov Ra(reg), [0x4]
| mov Ra(reg), aword [Ra(reg)+insn->op2]
| mov Ra(reg), aword [Ra(reg)+insn->op3]
|| }
| .endif
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
ir_emit_store(ctx, IR_ADDR, def, reg);
}
}
static int ir_emit_dessa_move(ir_ctx *ctx, uint8_t type, ir_ref from, ir_ref to)
{
ir_backend_data *data = ctx->data;
@ -6988,6 +7065,9 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr)
case IR_GUARD_FALSE:
ir_emit_guard(ctx, i, insn);
break;
case IR_TLS:
ir_emit_tls(ctx, i, insn);
break;
default:
IR_ASSERT(0 && "NIY rule/insruction");
break;