Clang と GCC の主な相違点

Clang と GCC は大部分で互換性がありますが、ここでは異なる部分を解説します。細かい所まで含めると膨大になるので、ここでは SOLID プロジェクトで実際に問題になったものだけを記載しています。

注釈

s008(exeClang)以降は Clang と LLVM lld のみのサポートとなります。以下の記述は exeGCC s007 以前にのみ当てはまります。

コンパイラドライバ

GCC は、コンパイラドライバである gcc.exeg++.exe が渡されたオプションを解析し、実際のコンパイラ cc1.exe (C) や cc1plus.exe (C++)、アセンブラ (Binutils の as.exe)、リンカ (Binutils の ld.exe) に適切なオプションを指定して呼び出すという仕組みになっています。

一方 Clang コンパイラはオプションによって、単一の exe (C の場合は clang.exe、C++ の場合は clang++.exe) がコンパイラドライバ、コンパイラ、アセンブラの役割を果たすという構造になっています。また、SOLID ツールチェーンの設定では、リンク時には gcc.exe あるいは g++.exe を呼び出してリンクを行います。そのため、リンク時に、GCC がサポートしないオプションを渡すとエラーになる場合があります。

注釈

s007 より Clang コンパイラのみ LLVM のリンカ ld.lld.exe をサポートしました。リンクに lld を使用する場合、GCC は呼び出されません。

文字コード

GCC は CPP -finput-charset/CPP -fexec-charsetcp932 (Shift_JIS) 等を指定し、ソースコードやアセンブラ出力の文字コードをデフォルトの UTF-8 から変更できますが、Clang は常に UTF-8 のみとなります。

pragma, attribute 拡張機能

標準プラグマ

以下の STDC プラグマは C99 で標準化されているので、本来は GCC/Clang 両方で使用できるはずです。

#pragma STDC FENV_ACCESS ON/OFF/DEFAULT
#pragma STDC FP_CONTRACT ON/OFF/DEFAULT
#pragma STDC CX_LIMITED_RANGE ON/OFF/DEFAULT

しかし GCC は、AARCH64/ARM アーキテクチャでは、全ての標準プラグマが無視されます。(-Wall 指定時に警告が出ます。)Clang は FENV_ACCESS のみ警告が出ますが、他の 2 つが正しく実装されているかどうかは未検証です。exeGCC では未サポート扱いとします。

注釈

exeClang s008 の RISC-V アーキテクチャでは、FENV_ACCESS と FP_CONTRACT の動作が確認されています。(CX_LIMITED は情報が少なく、効果を確認できていません。)以下のサンプルコードも参考にしてください。

#pragma STDC FENV_ACCESS のサンプルコード

#pragma STDC FP_CONTRACT のサンプルコード

よく使われる例

pragma や attribute は基本的にそれぞれのコンパイラに固有のものとなります。できるだけ使用しないようにしてください。特に #pragma GCC ... の形式のプラグマは GCC のみ、 #pragma clang ... の形式のプラグマは Clang のみとなります。GCC と Clang の共通コードで pragma や attribute を使用する場合は、以下の例のように #ifdef __clang__ で切り替えてください。

GCC で、特定の関数のみ最適化オフする __attribute__((optimize("O0"))) は、Clang では __attribute__((optnone)) となります。GCC は逆に、特定の関数のみ任意の最適化レベルを指定することもできますが、Clang はできません。

#ifdef __clang__
__attribute__((optnone))
#else
__attribute__((optimize("O0")))
#endif
void foo() { ... }

GCC で、最適化オフの範囲指定 #pragma GCC optimize("O0") は、Clang では #pragma clang optimize off となります。Clang の #pragma clang optimize XXX では、最適化の onoff の指定のみ可能で、GCC のように最適化レベルは指定できません。また、on を指定した場合でも、最適化オプションが指定されていない場合は、最適化は行われません。

naked attribute

この attribute は(AARCH64/ARM は)Clang のみのサポートとなります。

long __attribute__((naked)) add_one(long a)
{
#if defined(__aarch64__)
  __asm__ __volatile__("add x0, x0, #1; ret");
#elif defined(__arm__)
  __asm__ __volatile__("add r0, r0, #1; bx lr");
#endif
}

これをコンパイルすると、Clang は以下のようにインラインアセンブラ構文で記述されたコードのみが生成されます。

0000000000000000 <add_one>:
   0:   91000400        add     x0, x0, #0x1
   4:   d65f03c0        ret
00000000 <add_one>:
   0:   f100 0001       add.w   r0, r0, #1
   4:   4770            bx      lr

しかし GCC はそれ以外のコードも生成されています。

00000000 <add_one>:
   0:   f100 0001       add.w   r0, r0, #1
   4:   4770            bx      lr
   6:   bf00            nop
   8:   4618            mov     r0, r3

注釈

アライメント調整のため、Clang でも記述していない nop 命令が生成されることがあります。

AARCH64 では正しく未サポート(無視)の警告が出ますが、ARM では出ません。

C:\GCC4\AARCH64\s007\test>gcc -c add_one.c
add_one.c:2:1: warning: 'naked' attribute directive ignored [-Wattributes]
    2 | {
      |

C:\GCC4\AARCH64\s007\test>objdump -d add_one.o

add_one.o:     file format elf64-littleaarch64


Disassembly of section .text:

0000000000000000 <add_one>:
   0:   d10043ff        sub     sp, sp, #0x10
   4:   f90007e0        str     x0, [sp, #8]
   8:   91000400        add     x0, x0, #0x1
   c:   d65f03c0        ret
  10:   d503201f        nop
  14:   910043ff        add     sp, sp, #0x10
  18:   d65f03c0        ret

AARCH64 は関数プロローグ・エピローグコードまで生成されているので、全くサポート(機能の実装が)されておらず、ARM は中途半端にサポートされているような印象です。ここらへんは以下の議論から進展が無いようです。(上記の確認コードは、以下の議論中に出てきたコードを使用しています。)

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77882

-mthumb-interwork オプション

このオプションは Clang には存在しないので、Clang にパッチを当て、無視するようにしています。Clang では常に ARM/Thumb interworking が有効なコードが生成されます。

-marm オプション

Clang にこのオプションを渡すと、内部で -mno-thumb と解釈されます。SOLID ツールチェーンの Clang は GCC をリンカとして使用しているため、GCC が未サポートの -mno-thumb オプションが GCC に渡されてしまうとリンクエラーになるため、Clang にパッチを当て、無視するようにしています。そのため Clang では、デフォルトで -mthumb を指定しておいて、ARM でコンパイルしなければいけないファイルのみ -marm を付けるということはできなくなっています。その場合は -mno-thumb を指定してください。(s002 以降)

specs ファイル

GCC は specs ファイルによりコンパイラのデフォルト設定を変更可能ですが、Clang には specs ファイル機能が存在しないので -specs オプションは無視されます。

ただし、exeGCC の Clang は GCC をリンカとして使用するため、-specs は Clang から呼び出された GCC にそのまま渡され(リンカに関する設定のみ)期待通りに機能します。GCC に渡されるオプションは Clang に -v オプションを渡すことで確認できます。(Clang のリンカに LLVM の lld を使用した場合は specs ファイルは機能しません。)

arm-*-eabi ターゲット時の enum サイズの仕様

GCC は arm-*-eabi ターゲット時は aapcs ABI となり、enum が取り得る値の範囲に応じた可変長バイトとなりますが、Clang は固定長(4 もしくは 8 バイト)です。これは Linux(arm-*-gnueabi* ターゲット)の aapcs-linux ABI と同等の設定になっています。SOLID ではわかりやすさや互換性等を考慮し、Clang の方に合わせた設定となっています。(s002 以降)