GCC-3.4.6源代码学习笔记(167)

5.13.5.2.              为待定变量发布汇编

回到 finish_file ,我们已经看到 flag_unit_at_a_time 在‘ -O2 ’以上优化级别中设置为 1 。这个标记也可以由选项 -funit-at-a-time 来设定,【 6 】对其描述如下:

在开始产生代码之前,解析整个编译单元。这允许进行某些额外的优化,不过会消耗更多内存(总体而言)。使用 unit-at-a-time 模式有一些兼容性问题:

Ÿ           使能 unit-at-a-time 模式可能改变函数,变量及最顶层汇编语句的发布次序,并将很可能破坏依赖于特定次序的代码。然而大多数这样的最顶层汇编语句可以为段属性所替代。选项‘ fno-toplevel-reorder ’可以被用来保持输入文件中的次序,以某些优化作为代价。

Ÿ           unit-at-a-time 模式移除未被引用的静态变量及函数。当一个汇编语句直接指向除此之外不被使用的变量或函数时,这可能导致未定义的引用。在这个情形下,要么把该变量、函数列为这个汇编语句的一个操作数,或者在顶层汇编语句的情形下,属性 used 应该用在声明中。

Ÿ           静态函数现在可以使用破坏直接调用函数的汇编语句的非标准传入约定。再一次,属性 used 将防止这个行为。

作为一个临时的变通,可以使用‘ -fno-unit-at-a-time ’,不过这个方案可能不会被今后的 GCC 支持。

 

finish_file (continue)

 

2850   if (flag_unit_at_a_time )

2851   {

2852     cgraph_finalize_compilation_unit ();

2853     cgraph_optimize ();

2854   }

 

为执行优化,我们需要知道代码间的关系,然后我们可以根据探测出的关系剥除重复的部分。因此在这里,下面的函数分析编译单元并构建一个图来描述其中的依赖关系,数据流及控制流。

 

368  void

369  cgraph_finalize_compilation_unit (void)                                                 in cgraphunit.c

370  {

371    struct cgraph_node *node;

372 

373    if (!flag_unit_at_a_time )

374    {

375      cgraph_assemble_pending_functions ();

376      return ;

377    }

378 

379    cgraph_varpool_assemble_pending_decls ();

380    if (!quiet_flag )

381      fprintf (stderr , "/nAnalyzing compilation unit/n");

 

记得如果声明在这个编译单元以外可见,它被记录入队列 cgraph_varpool_nodes_queue 。在这里如果这个队列不是空的,所保存的声明应该已经有汇编产生;否则就应该在这里产生。

 

612  bool

613  cgraph_varpool_assemble_pending_decls (void)                                            in cgraph.c

614  {

615    bool changed = false;

616 

617    while (cgraph_varpool_nodes_queue )

618    {

619      tree decl = cgraph_varpool_nodes_queue ->decl;

620      struct cgraph_varpool_node *node = cgraph_varpool_nodes_queue ;

621 

622      cgraph_varpool_nodes_queue = cgraph_varpool_nodes_queue ->next_needed;

623      if (!TREE_ASM_WRITTEN (decl))

624      {

625        assemble_variable (decl, 0, 1, 0);

626        changed = true;

627      }

628      node->next_needed = NULL;

629    }

630    return changed;

631  }

 

上面的 623 行,在一个 VAR_DECL 中, TREE_ASM_WRITTEN 如果不是 0 ,表示已经写入汇编代码;否则就要调用 assemble_variable 。注意自动变量不使用这个函数发布汇编。

5.13.5.2.1.        内存分配

在这次调用中,参数 don’t_output_data 0 ,表示确实输出初始值。那么在下面 1344 行,在当前这个版本中,对于所有的前端,钩子 prepare_assemble_variable 都是 NULL

 

1335 void

1336 assemble_variable (tree decl, int top_level ATTRIBUTE_UNUSED,          in varasm.c

1337                 int at_end ATTRIBUTE_UNUSED, int dont_output_data)

1338 {

1339   const char *name;

1340   unsigned int align;

1341   int reloc = 0;

1342   rtx decl_rtl;

1343

1344   if (lang_hooks .decls.prepare_assemble_variable)

1345     (*lang_hooks .decls.prepare_assemble_variable) (decl);

1346

1347   last_assemble_variable_decl = 0;

1348

1349   /* Normally no need to say anything here for external references,

1350     since assemble_external is called by the language-specific code

1351     when a declaration is first seen.  */

1352

1353   if (DECL_EXTERNAL (decl))

1354     return ;

1355

1356   /* Output no assembler code for a function declaration.

1357     Only definitions of functions output anything.  */

1358

1359   if (TREE_CODE (decl) == FUNCTION_DECL)

1360     return ;

1361

1362   /* Do nothing for global register variables.  */

1363   if (DECL_RTL_SET_P (decl) && GET_CODE (DECL_RTL (decl)) == REG)

1364   {

1365     TREE_ASM_WRITTEN (decl) = 1;

1366     return ;

1367   }

1368

1369   /* If type was incomplete when the variable was declared,

1370     see if it is complete now.  */

1371

1372   if (DECL_SIZE (decl) == 0)

1373     layout_decl (decl, 0);

1374

1375   /* Still incomplete => don't allocate it; treat the tentative defn

1376     (which is what it must have been) as an `extern' reference.  */

1377

1378   if (!dont_output_data && DECL_SIZE (decl) == 0)

1379   {

1380     error ("%Jstorage size of `%D' isn't known", decl, decl);

1381     TREE_ASM_WRITTEN (decl) = 1;

1382     return ;

1383   }

1384

1385   /* The first declaration of a variable that comes through this function

1386     decides whether it is global (in C, has external linkage)

1387     or local (in C, has internal linkage). So do nothing more

1388     if this function has already run.  */

1389

1390   if (TREE_ASM_WRITTEN (decl))

1391     return ;

1392

1393   /* Make sure targetm.encode_section_info is invoked before we set

1394     ASM_WRITTEN.  */

1395   decl_rtl = DECL_RTL (decl);

1396

1397   TREE_ASM_WRITTEN (decl) = 1;

1398

1399   /* Do no output if -fsyntax-only.  */

1400   if (flag_syntax_only )

1401     return ;

 

能从其初始值构建关联的 RTL 节点的节点必须是已完成的。在前端中,查看中间树节点是否已完成,是检查其尺寸。因为每个由 make_node 创建的节点总是具有尺寸 0 ;而仅在 layout_decl 中,才会计算并设置这个大小尺寸。

然后在 1395 行,宏 DECL_RTL 为这个 VAR_DECL 产生了下面的 rtx 节点,而变量 decl_rtl 则指向下面的 rtx MEM 节点(对于这个例子, VAR_DECL 必须不能被声明为 register )。

GCC-3.4.6源代码学习笔记(167)_第1张图片

5.13.5.2.1.        内存配置

那么在下面的代码片段中,开始配置为这个 VAR_DECL MEM 节点中分配的内存。

 

assemble_variable (continue)

 

1403   app_disable ();

1404

1405   if (! dont_output_data

1406       && ! host_integerp (DECL_SIZE_UNIT (decl), 1))

1407   {

1408     error ("%Jsize of variable '%D' is too large", decl, decl);

1409     return ;

1410   }

1411

1412   name = XSTR (XEXP (decl_rtl, 0), 0);

1413   if (TREE_PUBLIC (decl) && DECL_NAME (decl))

1414     notice_global_symbol (decl);

1415

1416   /* Compute the alignment of this data.  */

1417

1418   align = DECL_ALIGN (decl);

1419

1420   /* In the case for initialing an array whose length isn't specified,

1421     where we have not yet been able to do the layout,

1422     figure out the proper alignment now.  */

1423   if (dont_output_data && DECL_SIZE (decl) == 0

1424       && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)

1425     align = MAX (align, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (decl))));

1426

1427   /* Some object file formats have a maximum alignment which they support.

1428     I n particular, a.out format supports a maximum alignment of 4.  */

1429 #ifndef MAX_OFILE_ALIGNMENT

1430 #define MAX_OFILE_ALIGNMENT BIGGEST_ALIGNMENT

1431 #endif

1432   if (align > MAX_OFILE_ALIGNMENT)

1433   {

1434     warning ("%Jalignment of '%D' is greater than maximum object "

1435              "file alignment. Using %d", decl, decl,

1436             MAX_OFILE_ALIGNMENT/BITS_PER_UNIT);

1437     align = MAX_OFILE_ALIGNMENT;

1438   }

1439

1440   /* On some machines, it is good to increase alignment sometimes.  */

1441   if (! DECL_USER_ALIGN (decl))

1442   {

1443 #ifdef DATA_ALIGNMENT

1444     align = DATA_ALIGNMENT (TREE_TYPE (decl), align);

1445 #endif

1446 #ifdef CONSTANT_ALIGNMENT

1447     if (DECL_INITIAL (decl) != 0 && DECL_INITIAL (decl) != error_mark_node)

1448       align = CONSTANT_ALIGNMENT (DECL_INITIAL (decl), align);

1449 #endif

1450   }

1451

1452   /* Reset the alignment in case we have made it tighter, so we can benefit

1453     from it in get_pointer_alignment.  */

1454   DECL_ALIGN (decl) = align;

1455   set_mem_align (decl_rtl, align);

1456

1457   if (TREE_PUBLIC (decl))

1458     maybe_assemble_visibility (decl);

 

首先,作为 GNU 汇编器的语法,如果一个输入文件的第一行是 #NO_APP ,或者如果使用了选项‘ -f ’,空格及注释不会从输入文件中移走。在一个输入文件中,你可以要求在特定的部分移走空格及注释,通过在这个可能包含空格及注释的文本前加入一行 #APP ,并在该文本后加上一行 #NO_APP 。这个特性主要目的在于支持编译器中的汇编语句,编译器的输出是没有空格及注释的。在 C++ 中,这主要用于嵌入汇编器,并在函数作用域外的嵌入汇编器块的开头, GCC 将自动插入 #APP 。因此在 1403 行,如果使用了 #APP ,在发布汇编之前,首先通过 app_disable 使用 #NO_APP 关闭这个 #APP

 

284  void

285  app_disable (void)                                                                                      in final.c

286  {

287    if (app_on )

288    {

289      fputs (ASM_APP_OFF, asm_out_file );

290      app_on = 0;

291    }

292  }

 

全局变量 app_on 如果非 0 ,表示 #APP 在使用;而 ASM_APP_OFF ,对于绝大多数目标平台被定义为“ #NO_APP ”(在剩下的少数中,它被定义为空串)。

接着在 1414 行, notice_global_symbol 收集在这个编译单元中出现的第一个全局变量的名字,它将被用于为匿名名字空间产生内部名字。上面代码的最后部分则是确定该内存的对齐量,即便对于 Linux/x86 目标平台,在 1429 行, MAX_OFILE_ALIGNMENT 仍未定义,而被设置为下面的 BIGGEST_ALIGNMENT (定义为 32 )。

我们已经看到的,除非显式地使用属性在声明中指出特别的对齐量,节点中的总是 false 。而在通常的情形中,目标平台会定义 DATA_ALIGNMENT 来增加中等大小数据的对齐量,以使它适配更少的缓存线( cache line )。另外 CONSTANT_ALIGNMENT 也被目标平台定义来增加字符串常量的对齐量到字边界,使得调用‘ strcpy ’拷贝常量可以内联地执行。然后这个精心确定的对齐量将被设置入 rtx MEM 节点中,它将在后面指引汇编的发布。

 

1822 void

1823 set_mem_align (rtx mem, unsigned int align)                                                in emit-rtl.c

1824 {

1825   MEM_ATTRS (mem) = get_mem_attrs (MEM_ALIAS_SET (mem), MEM_EXPR (mem),

1826                                MEM_OFFSET (mem), MEM_SIZE (mem), align,

1827                                GET_MODE (mem));

1828 }

 

MEM_ATTRS rtx 提取 rtmem 域的内容,这个域具有以下定义。

 

99    typedef struct mem_attrs GTY(())                                                                      in rtl.h

100  {

101    HOST_WIDE_INT alias;          /* Memory alias set.  */

102    tree expr;                  /* expr corresponding to MEM.  */

103    rtx offset;                 /* Offset from start of DECL, as CONST_INT.  */

104    rtx size;                    /* Size in bytes, as a CONST_INT.  */

105    unsigned int align;             /* Alignment of MEM in bits.  */

106  } mem_attrs;

 

上面 MEM_ALIAS_SET MEM_EXPR MEM_OFFSET MEM_SIZE 提取结构体中相应的域(但如果 rtmem 域是空的返回 0 ,并看到在 make_decl_rtl gen_rtx_MEM 中,一个空指针被置入 rtmem 域)。因此 set_mem_align 的净效果是在 get_mem_attrs 的协助下更新保存对齐量的域。

 

291  static mem_attrs *

292  get_mem_attrs (HOST_WIDE_INT alias, tree expr, rtx offset, rtx size,           in emit-rtl.c

293               unsigned int align, enum machine_mode mode)

294  {

295    mem_attrs attrs;

296    void **slot;

297 

298    /* If everything is the default, we can just return zero.

299      This must match what the corresponding MEM_* macros return when the

300      field is not present.  */

301    if (alias == 0 && expr == 0 && offset == 0

302       && (size == 0

303           || (mode != BLKmode && GET_MODE_SIZE (mode) == INTVAL (size)))

304       && (STRICT_ALIGNMENT && mode != BLKmode

305           ? align == GET_MODE_ALIGNMENT (mode) : align == BITS_PER_UNIT))

306      return 0;

307 

308    attrs.alias = alias;

309    attrs.expr = expr;

310    attrs.offset = offset;

311    attrs.size = size;

312    attrs.align = align;

313 

314    slot = htab_find_slot (mem_attrs_htab , &attrs, INSERT);

315    if (*slot == 0)

316    {

317      *slot = ggc_alloc (sizeof (mem_attrs));

318      memcpy (*slot, &attrs, sizeof (mem_attrs));

319    }

320 

321    return *slot;

322  }

 

mem_attrs 的所有实例都被缓存在哈希表 mem_attrs_htab 中以加速查找;并且每个内存属性设置是单件。看到通常地,相同类型的每个声明将具有相同的内存属性设置。

回到 assemble_variable ,在 1458 行, maybe_assemble_visibility 只是处理具有属性“ visibility ”的声明。该函数将调用后端的钩子来输出具有所描述可见性( visibility )的名字。我们在这里跳过其处理,【 6 】对这个属性给出了详细的描述。

你可能感兴趣的:(汇编,function,tree,alignment,output,compilation)