hexagon-clang 编译器下的 int 和 int32_t 不同导致的坑

hexagon-clang 编译器下的 int 和 int32_t 不同导致的坑

1. 问题描述

原本在 Linux-x64, Windows, 各种嵌入式 arm linux 平台能编译通过的代码, 在 Hexagon SDK 5.5.0.1 (hexagon-clang 8.7.06) 上出现编译报错:

error: call to ‘saturate_cast’ is ambiguous
saturate_cast<uint8_t>(a);
^~~~~~~~~~~~~~~~~~~~~~

排查发现 hexagon-clang 编译器下, intint32 并不是相同的类型, intlong 是相同类型, 并且 sizeof(long)=4, 这和常见的 Linux 中是不一样的。

2. 最小复现代码

#include <stdint.h>
#include <stdio.h>

template<typename T> static inline T saturate_cast(uint32_t v)  { return T(v); }
template<typename T> static inline T saturate_cast(int32_t v)   { return T(v); }

int main()
{
    int a = 233;
    saturate_cast<uint8_t>(a);

    return 0;
}

编译器版本:

root@2bfad214a508:~# /opt/Hexagon_SDK/5.5.0.1/tools/HEXAGON_Tools/8.7.06/Tools/bin/hexagon-clang++ --version   
QuIC LLVM Hexagon Clang version 8.7.06
Target: hexagon
Thread model: posix
InstalledDir: /opt/Hexagon_SDK/5.5.0.1/tools/HEXAGON_Tools/8.7.06/Tools/bin

注意,这个版本 8.7.06 死后 QuIC LLVM 的版本, 查看 hexagon-clang 内置的宏,会发现它其实是 clang 15.0.0。

执行编译:

root@2bfad214a508:~# /opt/Hexagon_SDK/5.5.0.1/tools/HEXAGON_Tools/8.7.06/Tools/bin/hexagon-clang++ ./test5.cpp 
./test5.cpp:10:5: error: call to 'saturate_cast' is ambiguous
    saturate_cast<uint8_t>(a);
    ^~~~~~~~~~~~~~~~~~~~~~
./test5.cpp:4:38: note: candidate function [with T = unsigned char]
template<typename T> static inline T saturate_cast(uint32_t v)  { return T(v); }
                                     ^
./test5.cpp:5:38: note: candidate function [with T = unsigned char]
template<typename T> static inline T saturate_cast(int32_t v)   { return T(v); }
                                     ^
1 error generated.

在 Godbolt 上目前(2024-01-29 09:34:11)只有一个 hexagon-clang 编译器, 但是版本是 16.0.5, 无法发现上述问题: https://godbolt.org/z/nsb36rPcx

3. 编译期判断两个类型是否相同

C++11 提供了 std::is_same<T, U> 来判断类型 T 和 U 是否相同: https://en.cppreference.com/w/cpp/types/is_same 。

如果类型相同, 那么 value 属性为 true, 否则为 false。

std::is_same<T, U>::value 是编译期确定的。

3.1 运行期输出

也就是:

if (is_same<T, U>::value == true)
{
    printf("T and U is same type\n");
}
else
{
    printf("T and U is not same type\n");
}

上面这段代码的 printf 是运行期输出, 可以通过 hexagon-sim 这个模拟器运行输出:

root@2bfad214a508:~# /opt/Hexagon_SDK/5.5.0.1/tools/HEXAGON_Tools/8.7.06/Tools/bin/hexagon-sim ./a.out 
hexagon-sim INFO: The rev_id used in the simulation is 0x00008d68 (v68n_1024)
int is not int32_t

Done!
        T0: Insns=9562 Packets=4713
        T1: Insns=0 Packets=0
        T2: Insns=0 Packets=0
        T3: Insns=0 Packets=0
        T4: Insns=0 Packets=0
        T5: Insns=0 Packets=0
        Total: Insns=9562 Pcycles=14142

3.2 编译期输出

C++11 提供了 static_assert 在编译期执行 assert 和打印: https://en.cppreference.com/w/cpp/language/static_assert

static_assert( bool-constexpr , message )		(since C++11)

If bool-constexpr is well-formed and evaluates to true, or is evaluated in the context of a template definition and the template is uninstantiated, this declaration has no effect. Otherwise a compile-time error is issued, and the text of message, if any, is included in the diagnostic message.

如果 static_assert() 里的条件表达式结果为 true, 则什么都不输出; 如果条件为 false, 则输出 message 变量的内容。

因此写下如下的编译期代码:

#include <stdint.h>
#include <stdio.h>
#include <type_traits>

template<typename T> static inline T saturate_cast(uint32_t v)  { return T(v); }
template<typename T> static inline T saturate_cast(int32_t v)   { return T(v); }

int main()
{
    //int a = 233;
    //saturate_cast<uint8_t>(a);

    static_assert(std::is_same<int, int32_t>::value, "int is not int32_t");
    static_assert(std::is_same<int, long>::value, "int is not long");
    static_assert(!std::is_same<int32_t, long>::value, "int32_t is same as long");

    return 0;
}

编译结果:

root@2bfad214a508:~# /opt/Hexagon_SDK/5.5.0.1/tools/HEXAGON_Tools/8.7.06/Tools/bin/hexagon-clang++ ./test5.cpp 
./test5.cpp:13:5: error: static assertion failed due to requirement 'std::is_same<int, long>::value': int is not int32_t
    static_assert(std::is_same<int, int32_t>::value, "int is not int32_t");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./test5.cpp:14:5: error: static assertion failed due to requirement 'std::is_same<int, long>::value': int is not long
    static_assert(std::is_same<int, long>::value, "int is not long");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./test5.cpp:15:5: error: static assertion failed due to requirement '!std::is_same<long, long>::value': int32_t is same as long
    static_assert(!std::is_same<int32_t, long>::value, "int32 is same as long");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 errors generated.

而如果是 Linux-x64 上的 GCC11, 编译结果则是:

test5.cpp: In function ‘int main():
test5.cpp:14:44: error: static assertion failed: int is not long
   14 |     static_assert(std::is_same<int, long>::value, "int is not long");
      |                   ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~

3.4 编译期查看 sizeof(long)

#include <stdint.h>
#include <stdio.h>
#include <type_traits>

template<typename T> static inline T saturate_cast(uint32_t v)  { return T(v); }
template<typename T> static inline T saturate_cast(int32_t v)   { return T(v); }

#include <cstddef>
template <std::size_t x>
struct show_size;

void foo()
{
    show_size<sizeof(long)>();
    show_size<sizeof(int*)>();
}

int main()
{
    //int a = 233;
    //saturate_cast<uint8_t>(a);

    // static_assert(std::is_same<int, int32_t>::value, "int is not int32_t");
    // static_assert(std::is_same<int, long>::value, "int is not long");
    // static_assert(!std::is_same<int32_t, long>::value, "int32 is same as long");

    return 0;
}

编译结果:

root@2bfad214a508:~# /opt/Hexagon_SDK/5.5.0.1/tools/HEXAGON_Tools/8.7.06/Tools/bin/hexagon-clang++ ./test5.cpp 
./test5.cpp:14:5: error: implicit instantiation of undefined template 'show_size<4>'
    show_size<sizeof(long)>();
    ^
./test5.cpp:10:8: note: template is declared here
struct show_size;
       ^
./test5.cpp:15:5: error: implicit instantiation of undefined template 'show_size<4>'
    show_size<sizeof(int*)>();
    ^
./test5.cpp:10:8: note: template is declared here
struct show_size;
       ^
2 errors generated.

说明 sizeof(long) 在 hexagon-clang 编译器下等于4, 指针类型在 hexagon-clang 编译器下也等于 4 字节, 是32位的编译器。

3.5 hexagon 上的类型的结论

int 和 int32 不是同一个类型。

int 和 long 也不是同一个类型。

int32_t 和 long 是同一个类型。

hexagon-clang 是32位而不是64位的指令集架构输出。

4. 编译期给 hexagon-clang 打补丁

对于现有代码, 要在 hexagon-clang 平台编译通过, 需要针对 hexagon-clang 编译器打补丁。

4.1 通过 __hexagon__ 宏判断

查看 hexagon-clang 编译器默认提供的宏定义:

root@2bfad214a508:~# /opt/Hexagon_SDK/5.5.0.1/tools/HEXAGON_Tools/8.7.06/Tools/bin/hexagon-clang++ -dM -E -x c++ - < /dev/null | grep -i 'hexagon'
#define __HEXAGON_ARCH__ 68
#define __HEXAGON_PHYSICAL_SLOTS__ 4
#define __HEXAGON_V68__ 1
#define __VERSION__ "Clang 15.0.0 (/local/mnt/workspace/bots/llvm-release-bot-sles12_lv_5/hexagon-clang-87/build/llvm-top/clang 011f725a1454ed0d47d2ccfbb9097eb99743bf21)"
#define __clang_version__ "15.0.0 (/local/mnt/workspace/bots/llvm-release-bot-sles12_lv_5/hexagon-clang-87/build/llvm-top/clang 011f725a1454ed0d47d2ccfbb9097eb99743bf21)"
#define __hexagon__ 1

因此使用 __hexagon__ 来做判断:

#include <stdint.h>
#include <stdio.h>

template<typename T> static inline T saturate_cast(uint32_t v)  { return T(v); }
template<typename T> static inline T saturate_cast(int32_t v)   { return T(v); }

#if __hexagon__
template<typename T> static inline T saturate_cast(int v)   { return T(v); }
#endif

int main()
{
    int a = 233;
    saturate_cast<uint8_t>(a);
    return 0;
}

4.2 使用 enable_if 执行条件编译

C++11 提供了 enable_if: https://en.cppreference.com/w/cpp/types/enable_if

template< bool B, class T = void >
struct enable_if;

If B is true, std::enable_if has a public member typedef type, equal to T; otherwise, there is no member typedef.

如果 B 是 true, 那么 std::enable_if 会有一个公共成员 type, 它等于 T;如果 B 为 false, 则没有这个成员。

因此当编译期 B 为 true 就可以选则 T 类型, 进而定义函数; 当编译期 B 为 false, 就不会定义函数:

#include <stdint.h>
#include <stdio.h>
#include <type_traits>

template<typename T> static inline T saturate_cast(uint32_t v)  { return T(v); }
template<typename T> static inline T saturate_cast(int32_t v)   { return T(v); }

// #if __hexagon__
// template<typename T> static inline T saturate_cast(int v)   { return T(v); }
// #endif

template<typename T> static inline 
typename std::enable_if<!std::is_same<int, int32_t>::value, T>::type saturate_cast(int v)
{
    return T(v);
}

int main()
{
    int a = 233;
    saturate_cast<uint8_t>(a);
    return 0;
}

解释: 在编译期, 如果 int 和 int32_t 类型不同, 那么让编译器得到类型 T, 进而定义了如下函数:

template<typename T> static inline 
typename T saturate_cast(int v)
{
    return T(v);
}

而如果 int 和 int32_t 类型相同, 那么就不会执行这个函数的编译了。

5. 类型提升导致的新坑

当两个 unsigned char 相加, 结果类型会是 int 类型, 这是类型提升。 此时在 hexagon-clang 下会再次触发模板函数如 saturate_cast 的匹配失败(假设没有做前面增加的 “补丁”):

#include <stdint.h>
#include <stdio.h>
#include <type_traits>

int main()
{
    unsigned char a = 3;
    unsigned char b = 4;
    auto c = a + b;
    static_assert(std::is_same<decltype(c), int>::value, "c is not int");
    static_assert(std::is_same<decltype(c), int32_t>::value, "c is not int32_t");

    return 0;
}

编译输出:

root@2bfad214a508:~# /opt/Hexagon_SDK/5.5.0.1/tools/HEXAGON_Tools/8.7.06/Tools/bin/hexagon-clang++ ./test5.cpp 
./test5.cpp:41:5: error: static assertion failed due to requirement 'std::is_same<int, long>::value': c is not int32_t
    static_assert(std::is_same<decltype(c), int32_t>::value, "c is not int32_t");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

其中报错是说 c 的类型是 int, 但不是 int32_t.

也尝试了使用 uint8_t a=3, uint8_t b=4, 但是结果是一样的。

因此, 不仅仅是 saturate_cast 模板函数本身要对 int 和 int32_t 同时做处理, 对于其他模板函数也有同样的问题。

6. 结论

总结一下 C++ 方面的技巧:

  • std::is_same<T, U>::value 是个好东西,编译期的
  • static_assert 也是个好东西,编译期的
  • std::enable_if 是做条件编译,也挺好用的

总结一下 hexagon-clang 的坑:

  • int 和 int32_t 不是同一个类型
  • 遇到 “ambibuous” 的报错,需要检查模板相关的类型报错
  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值