From 3dd4d9c1212cc4dc624822003426d78de1671b63 Mon Sep 17 00:00:00 2001 From: Michael Beck Date: Thu, 31 Aug 2006 12:10:43 +0000 Subject: [PATCH] Add support for Linux TLS --- ir/be/ia32/bearch_ia32.c | 17 ++++++ ir/be/ia32/ia32_emitter.c | 39 ++++++++++++- ir/be/ia32/ia32_emitter.h | 3 +- ir/be/ia32/ia32_gen_decls.c | 110 +++++++++++++++++++++++++----------- ir/be/ia32/ia32_new_nodes.c | 31 ++++------ ir/be/ia32/ia32_new_nodes.h | 10 ---- ir/be/ia32/ia32_spec.pl | 29 +++++----- 7 files changed, 159 insertions(+), 80 deletions(-) diff --git a/ir/be/ia32/bearch_ia32.c b/ir/be/ia32/bearch_ia32.c index 64dc573d4..989be6e80 100644 --- a/ir/be/ia32/bearch_ia32.c +++ b/ir/be/ia32/bearch_ia32.c @@ -801,6 +801,22 @@ static void ia32_kill_convs(ia32_code_gen_t *cg) { } } +/** + * Transform the Thread Local Store base. + */ +static void transform_tls(ir_graph *irg) { + ir_node *irn = get_irg_tls(irg); + + if (irn) { + dbg_info *dbg = get_irn_dbg_info(irn); + ir_node *blk = get_nodes_block(irn); + ir_node *newn; + newn = new_rd_ia32_LdTls(dbg, irg, blk, get_irn_mode(irn)); + + exchange(irn, newn); + } +} + /** * Transforms the standard firm graph into * an ia32 firm graph @@ -820,6 +836,7 @@ static void ia32_prepare_graph(void *self) { dom = be_compute_dominance_frontiers(cg->irg); cg->kill_conv = new_nodeset(5); + transform_tls(cg->irg); irg_walk_blkwise_graph(cg->irg, NULL, ia32_transform_node, cg); ia32_kill_convs(cg); del_nodeset(cg->kill_conv); diff --git a/ir/be/ia32/ia32_emitter.c b/ir/be/ia32/ia32_emitter.c index c69e6f14d..282fb6723 100644 --- a/ir/be/ia32/ia32_emitter.c +++ b/ir/be/ia32/ia32_emitter.c @@ -49,10 +49,18 @@ void ia32_switch_section(FILE *F, section_t sec) { static section_t curr_sec = NO_SECTION; static const char *text[ASM_MAX][SECTION_MAX] = { { - ".section\t.text", ".section\t.data", ".section\t.rodata", ".section\t.text" + ".section\t.text", + ".section\t.data", + ".section\t.rodata", + ".section\t.text", + ".section\t.tbss,\"awT\",@nobits" }, { - ".section\t.text", ".section\t.data", ".section .rdata,\"dr\"", ".section\t.text" + ".section\t.text", + ".section\t.data", + ".section .rdata,\"dr\"", + ".section\t.text", + ".section\t.tbss,\"awT\",@nobits" } }; @@ -69,6 +77,7 @@ void ia32_switch_section(FILE *F, section_t sec) { case SECTION_DATA: case SECTION_RODATA: case SECTION_COMMON: + case SECTION_TLS: fprintf(F, "\t%s\n", text[asm_flavour][sec]); break; @@ -1802,7 +1811,7 @@ static void emit_ia32_Const(const ir_node *n, ia32_emit_env_t *env) { * Emits code to increase stack pointer. */ static void emit_ia32_AddSP(const ir_node *irn, ia32_emit_env_t *emit_env) { - FILE *F = emit_env->out; + FILE *F = emit_env->out; char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; if (is_ia32_ImmConst(irn)) { @@ -1822,6 +1831,29 @@ static void emit_ia32_AddSP(const ir_node *irn, ia32_emit_env_t *emit_env) { IA32_DO_EMIT(irn); } +/** + * Emits code to load the TLS base + */ +static void emit_ia32_LdTls(const ir_node *irn, ia32_emit_env_t *emit_env) { + FILE *F = emit_env->out; + char cmd_buf[SNPRINTF_BUF_LEN], cmnt_buf[SNPRINTF_BUF_LEN]; + + switch (asm_flavour) { + case ASM_LINUX_GAS: + lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "mov %1D, DWORD PTR %%gs:0", irn); + break; + case ASM_MINGW_GAS: + lc_esnprintf(ia32_get_arg_env(), cmd_buf, SNPRINTF_BUF_LEN, "mov %1D, DWORD PTR %%gs:0", irn); + break; + default: + assert(0 && "unsupported TLS"); + break; + } + snprintf(cmnt_buf, SNPRINTF_BUF_LEN, "/* get thread local storage base */"); + + IA32_DO_EMIT(irn); +} + static void emit_be_Return(const ir_node *n, ia32_emit_env_t *env) { FILE *F = env->out; const lc_arg_env_t *arg_env = ia32_get_arg_env(); @@ -1884,6 +1916,7 @@ static void ia32_register_emitters(void) { IA32_EMIT(Conv_I2I8Bit); IA32_EMIT(Const); IA32_EMIT(AddSP); + IA32_EMIT(LdTls); IA32_EMIT(xCmp); IA32_EMIT(xCmpSet); IA32_EMIT(xCmpCMov); diff --git a/ir/be/ia32/ia32_emitter.h b/ir/be/ia32/ia32_emitter.h index 6c2453382..3f640050f 100644 --- a/ir/be/ia32/ia32_emitter.h +++ b/ir/be/ia32/ia32_emitter.h @@ -46,7 +46,8 @@ typedef enum section_t { SECTION_DATA = 1, /**< data section */ SECTION_RODATA = 2, /**< rodata section */ SECTION_COMMON = 3, /**< common section */ - SECTION_MAX = 4 + SECTION_TLS = 4, /**< thread local storage section */ + SECTION_MAX = 5 } section_t; /** diff --git a/ir/be/ia32/ia32_gen_decls.c b/ir/be/ia32/ia32_gen_decls.c index ec32df8bc..6fa5d88ff 100644 --- a/ir/be/ia32/ia32_gen_decls.c +++ b/ir/be/ia32/ia32_gen_decls.c @@ -70,8 +70,8 @@ static void ia32_dump_comm(struct obstack *obst, const char *name, visibility vi } } -/* - * output the alignment +/** + * output the alignment to an obstack */ static void ia32_dump_align(struct obstack *obst, int align) { @@ -85,6 +85,24 @@ static void ia32_dump_align(struct obstack *obst, int align) obstack_printf(obst, "\t.align %d\n", align); } +/** + * output the alignment to a FILE + */ +static void ia32_dump_align_f(FILE *f, int align) +{ + int h = highest_bit(align); + + if ((1 << h) < align) + ++h; + align = (1 << h); + + if (align > 1) + fprintf(f, "\t.align %d\n", align); +} + +/** + * output a tarval + */ static void dump_arith_tarval(struct obstack *obst, tarval *tv, int bytes) { switch (bytes) { @@ -335,6 +353,9 @@ struct arr_info { int size; }; +/** + * Dump the size of an object + */ static void dump_object_size(struct obstack *obst, const char *name, int size) { switch (asm_flavour) { case ASM_LINUX_GAS: @@ -356,8 +377,8 @@ static void dump_global(struct obstack *rdata_obstack, struct obstack *data_obst struct obstack *obst = data_obstack; /* - * FIXME: did NOT work for partly constant values - */ + * FIXME: did NOT work for partly constant values + */ if (! is_Method_type(ty)) { ent_variability variability = get_entity_variability(ent); visibility visibility = get_entity_visibility(ent); @@ -372,7 +393,7 @@ static void dump_global(struct obstack *rdata_obstack, struct obstack *data_obst if (visibility == visibility_external_visible) { obstack_printf(obst, ".globl\t%s\n", ld_name); } - dump_object_size(obst, ld_name, (get_type_size_bits(ty) + 7) >> 3); + dump_object_size(obst, ld_name, get_type_size_bytes(ty)); align = get_type_alignment_bytes(ty); ia32_dump_align(obst, align); @@ -419,14 +440,14 @@ static void dump_global(struct obstack *rdata_obstack, struct obstack *data_obst int type_size, j; /* Compound entities are NOT sorted. - * The sorting strategy used doesn't work for `value' compound fields nor - * for partially_constant entities. - */ + * The sorting strategy used doesn't work for `value' compound fields nor + * for partially_constant entities. + */ /* - * in the worst case, every entity allocates one byte, so the type - * size should be equal or bigger the number of fields - */ + * in the worst case, every entity allocates one byte, so the type + * size should be equal or bigger the number of fields + */ type_size = get_type_size_bytes(ty); vals = xcalloc(type_size, sizeof(*vals)); @@ -442,7 +463,7 @@ static void dump_global(struct obstack *rdata_obstack, struct obstack *data_obst ai = xcalloc(graph_length, sizeof(struct arr_info)); /* We wanna know how many arrays are on the path to the entity. We also have to know how - * many elements each array holds to calculate the offset for the entity. */ + * many elements each array holds to calculate the offset for the entity. */ for (j = 0; j < graph_length; j++) { entity *step = get_compound_graph_path_node(path, j); ir_type *step_type = get_entity_type(step); @@ -515,33 +536,44 @@ static void dump_global(struct obstack *rdata_obstack, struct obstack *data_obst obstack_printf(obst, "\n"); } else if (visibility != visibility_external_allocated) { - /* calculate the alignment */ - align = get_type_alignment_bytes(ty); - h = highest_bit(align); - - if ((1 << h) < align) - ++h; - align = (1 << h); - - if (align < 1) - align = 1; - - ia32_dump_comm(comm_obstack, ld_name, visibility, - (get_type_size_bits(ty) + 7) >> 3, align); + /* uninitialized and NOT external */ + if (get_entity_owner(ent) != get_tls_type()) { + /* calculate the alignment */ + align = get_type_alignment_bytes(ty); + h = highest_bit(align); + + if ((1 << h) < align) + ++h; + align = (1 << h); + + if (align < 1) + align = 1; + + ia32_dump_comm(comm_obstack, ld_name, visibility, + get_type_size_bytes(ty), align); + } else { + /* TLS */ + if (visibility == visibility_external_visible) { + obstack_printf(obst, ".globl\t%s\n", ld_name); + } + dump_object_size(comm_obstack, ld_name, get_type_size_bytes(ty)); + align = get_type_alignment_bytes(ty); + ia32_dump_align(obst, align); + obstack_printf(comm_obstack, "%s:\n\t.zero %d\n", ld_name, get_type_size_bytes(ty)); + } } } } -/* +/** * Dumps declarations of global variables and the initialization code. */ -void ia32_dump_globals(struct obstack *rdata_obstack, struct obstack *data_obstack, struct obstack *comm_obstack) +static void ia32_dump_globals(ir_type *gt, struct obstack *rdata_obstack, struct obstack *data_obstack, struct obstack *comm_obstack) { - ir_type *gt = get_glob_type(); - int i, n = get_class_n_members(gt); + int i, n = get_compound_n_members(gt); for (i = 0; i < n; i++) - dump_global(rdata_obstack, data_obstack, comm_obstack, get_class_member(gt, i)); + dump_global(rdata_obstack, data_obstack, comm_obstack, get_compound_member(gt, i)); } /************************************************************************/ @@ -551,11 +583,12 @@ void ia32_gen_decls(FILE *out) { int size; char *cp; + /* dump the global type */ obstack_init(&rodata); obstack_init(&data); obstack_init(&comm); - ia32_dump_globals(&rodata, &data, &comm); + ia32_dump_globals(get_glob_type(), &rodata, &data, &comm); size = obstack_object_size(&data); cp = obstack_finish(&data); @@ -567,7 +600,7 @@ void ia32_gen_decls(FILE *out) { size = obstack_object_size(&rodata); cp = obstack_finish(&rodata); if (size > 0) { - fprintf(out, "\t.section\t.rodata\n"); + ia32_switch_section(out, SECTION_RODATA); fwrite(cp, 1, size, out); } @@ -581,4 +614,17 @@ void ia32_gen_decls(FILE *out) { obstack_free(&rodata, NULL); obstack_free(&data, NULL); obstack_free(&comm, NULL); + + /* dump the Thread Local Storage */ + obstack_init(&data); + ia32_dump_globals(get_tls_type(), &data, &data, &data); + + size = obstack_object_size(&data); + cp = obstack_finish(&data); + if (size > 0) { + ia32_switch_section(out, SECTION_TLS); + ia32_dump_align_f(out, 32); + fwrite(cp, 1, size, out); + } + } diff --git a/ir/be/ia32/ia32_new_nodes.c b/ir/be/ia32/ia32_new_nodes.c index d077b3ad1..cd58e1cc4 100644 --- a/ir/be/ia32/ia32_new_nodes.c +++ b/ir/be/ia32/ia32_new_nodes.c @@ -42,14 +42,22 @@ * @return The ident of the SymConst */ static ident *get_sc_ident(ir_node *symc) { - assert(get_irn_opcode(symc) == iro_SymConst && "need symconst to get ident"); + entity *ent; + ir_type *owner; + ident *id; switch (get_SymConst_kind(symc)) { case symconst_addr_name: return get_SymConst_name(symc); case symconst_addr_ent: - return get_entity_ld_ident(get_SymConst_entity(symc)); + ent = get_SymConst_entity(symc); + owner = get_entity_owner(ent); + id = get_entity_ld_ident(ent); + if (owner == get_tls_type()) { + id = mangle(id, new_id_from_chars("@NTPOFF", 7)); + } + return id; default: assert(0 && "Unsupported SymConst"); @@ -678,23 +686,6 @@ void set_ia32_Immop_tarval(ir_node *node, tarval *tv) { attr->cnst = get_ident_for_tv(tv); } -/** - * Return the sc attribute. - */ -ident *get_ia32_sc(const ir_node *node) { - ia32_attr_t *attr = get_ia32_attr(node); - return attr->cnst_val.sc; -} - -/** - * Sets the sc attribute. - */ -void set_ia32_sc(ir_node *node, ident *sc) { - ia32_attr_t *attr = get_ia32_attr(node); - attr->cnst_val.sc = sc; - attr->cnst = attr->cnst_val.sc; -} - /** * Gets the string representation of the internal const (tv or symconst) */ @@ -1184,7 +1175,7 @@ void copy_ia32_Immop_attr(ir_node *dst, ir_node *src) { } /** - * Copy the attributes from a Firm Const to an ia32_Const + * Copy the attributes from a Firm Const/SymConst to an ia32_Const */ void set_ia32_Const_attr(ir_node *ia32_cnst, ir_node *cnst) { ia32_attr_t *attr = get_ia32_attr(ia32_cnst); diff --git a/ir/be/ia32/ia32_new_nodes.h b/ir/be/ia32/ia32_new_nodes.h index 58529da99..514852f82 100644 --- a/ir/be/ia32/ia32_new_nodes.h +++ b/ir/be/ia32/ia32_new_nodes.h @@ -126,16 +126,6 @@ tarval *get_ia32_Immop_tarval(const ir_node *node); */ void set_ia32_Immop_tarval(ir_node *node, tarval *tv); -/** - * Return the sc attribute. - */ -ident *get_ia32_sc(const ir_node *node); - -/** - * Sets the sc attribute. - */ -void set_ia32_sc(ir_node *node, ident *sc); - /** * Gets the string representation of the internal const (tv or symconst) */ diff --git a/ir/be/ia32/ia32_spec.pl b/ir/be/ia32/ia32_spec.pl index 72f273c45..0abe5d3fd 100644 --- a/ir/be/ia32/ia32_spec.pl +++ b/ir/be/ia32/ia32_spec.pl @@ -770,6 +770,14 @@ else { "outs" => [ "stack", "M" ], }, +"LdTls" => { + "irn_flags" => "R", + "comment" => "get the TLS base address", + "reg_req" => { "out" => [ "gp" ] }, +}, + + + #-----------------------------------------------------------------------------# # _____ _____ ______ __ _ _ _ # # / ____/ ____| ____| / _| | | | | | # @@ -1543,63 +1551,56 @@ else { # constants "fldz" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load 0.0: Ld 0.0 -> reg", "reg_req" => { }, "emit" => '. fldz /* x87 0.0 -> %D1 */', }, "fld1" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load 1.0: Ld 1.0 -> reg", "reg_req" => { }, "emit" => '. fld1 /* x87 1.0 -> %D1 */', }, "fldpi" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load pi: Ld pi -> reg", "reg_req" => { }, "emit" => '. fldpi /* x87 pi -> %D1 */', }, "fldln2" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load ln 2: Ld ln 2 -> reg", "reg_req" => { }, "emit" => '. fldln2 /* x87 ln(2) -> %D1 */', }, "fldlg2" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load lg 2: Ld lg 2 -> reg", "reg_req" => { }, "emit" => '. fldlg2 /* x87 log(2) -> %D1 */', }, "fldl2t" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load ld 10: Ld ld 10 -> reg", "reg_req" => { }, "emit" => '. fldll2t /* x87 ld(10) -> %D1 */', }, "fldl2e" => { - "op_flags" => "R", + "op_flags" => "R|c", "irn_flags" => "R", - "rd_constructor" => "NONE", "comment" => "x87 fp Load ld e: Ld ld e -> reg", "reg_req" => { }, "emit" => '. fldl2e /* x87 ld(e) -> %D1 */', -- 2.20.1