PART 03 编译器升级后出现的编译错误
★ MMERR打印宏引起编译器ICE(internel compiler error)

经过分析后确定是已知bug,[9 Regression] ICE in subspan, at input.h:69[2]。该bug属于前端(frontend),由于get_substring_ranges_for_loc 获得的位置信息中列号为0,导致subspan拿到的偏移量为-1。该问题修复patch包含在gcc10.3.0的更新中,tgcc升级基线后问题解决。
★ AutoFDO方式编译 jemalloc/src/prof.c时栈溢出错误
GCC的AutoFDO会将间接调用函数都内联到caller函数中,auto-profile.cc中对于递归函数并没有作特别处理,因此在遇到递归函数时会出现无限内联导致栈溢出Segment fault, GCC12中已经对这个问题作了修复,对于递归函数在AutoFDO的时候跳过内联即可。我们将这段代码移植到对应的GCC10分支后同样解决了这个问题。

★ gcc: ICE in ipa_profile_write_edge_summary
lto+autoFDO 统一编译的时候报ipa_profile_write_edge_summary 的ICE,但是单独编译无法复现。

调试后发现GCC在分配indirect_call采样灰度值变量的内存空间不足,autofdo的indirect call只填了四个counter, 但是ipa_profile_genereate_summary会按照TOPN的格式去访问数据,导致访问到第5至第9个未分配内存的区域,越界产生了随机行为,调整变量内存空间后修复了此问题。xgcc: fatal error: cannot execute '/data/mm64/mmdev/gcc10_debug/./gcc/cc1': execv: Argument list too long微信的bazel build需要传入非常多的-isystem 参数到gcc中作为预编译头文件。gcc driver在fork后invoke cc1plus的时候参数超过256K的时候就开始报参数过长的错误:“execv: Argument list too long” 但是系统的限制约为2M,单独的测试案例直接invoke cc1plus也只会在2M之后报参数过长,需要分析看GCC在什么地方做了额外的设置导致提前报错。内核对环境变量参数字符串长度有限制。execv在fork新的进程之前会调用copy_strings把argv和envp都拷贝到内核空间,argv的长度通过修改linux内核已经提高到2M了,但是envp的长度是由MAX_ARG_STRLEN来控制的,这个宏在devcloud机器上面是PAGESIZE*32, 也就是128K,但是在微信的编译机器上面变成了256K(微信编译机采用了修改过的Kernel,并没有用默认的MAX_ARG_STRLEN)

gcc在调用execv fork cc1前会调用libc的系统函数“putenv”设置一个很长的环境变量COLLECT_GCC_OPTIONS(相当于输入参数的长度),这个环境变量是GCC必须的,当COLLECT_GCC_OPTIONS的长度超过内核的限制时就会报参数过长的错误。建议的解决方案为:修改linux kernel拷贝环境变量的字符串长度限制。
★ profile-use+LTO ICE in lto-partion.c

这个ICE发生在LTO的WPA阶段,牵涉到了很多链接文件,非常的难查,社区里面也经常遇到,但是并没有特别好的解决办法[3]。视频号的推荐模块用到了2000多个静态库,包括了大量的重名函数分布在不同的库文件中,导致很难reduce成小的测试案例,不过通过隔离文件的方法找到了一种成功链接的结果用于对比,比较后发现在ipa-profile pass时两边同样的节点信息还是完全一致,但是ipa-visibility这个pass运行结束成功链接和失败链接对应函数节点的comdat group信息出现了差异,进一步跟踪后发现相同的函数节点和属性但是不同的访问顺序会导致comdat的消除出现不同的结果,原因是externally_visable变量在使用时还没有更新到预期的值,因此调整update_visibility_by_resolution_info中的检查函数来消除节点访问顺序造成的影响[4]。