Add support for Linux TLS
authorMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Thu, 31 Aug 2006 12:10:43 +0000 (12:10 +0000)
committerMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Thu, 31 Aug 2006 12:10:43 +0000 (12:10 +0000)
ir/be/ia32/bearch_ia32.c
ir/be/ia32/ia32_emitter.c
ir/be/ia32/ia32_emitter.h
ir/be/ia32/ia32_gen_decls.c
ir/be/ia32/ia32_new_nodes.c
ir/be/ia32/ia32_new_nodes.h
ir/be/ia32/ia32_spec.pl

index 64dc573..989be6e 100644 (file)
@@ -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);
index c69e6f1..282fb67 100644 (file)
@@ -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);
index 6c24533..3f64005 100644 (file)
@@ -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;
 
 /**
index ec32df8..6fa5d88 100644 (file)
@@ -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);
+       }
+
 }
index d077b3a..cd58e1c 100644 (file)
  * @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);
index 58529da..514852f 100644 (file)
@@ -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)
  */
index 72f273c..0abe5d3 100644 (file)
@@ -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 */',