本文へジャンプ

Hitachi

株式会社 日立超LSIシステムズ

SH C/C++コンパイラ Ver.7.1.00で対策した制限事項

1. スタックアクセス不正

現象:

レジスタ渡しパラメタのアドレスを参照すると、スタック上のデータを不正に上書きする場合がある。

[例]
  extern void er();
  extern char f2(char *a);
  void f(char a) {
      char c;
      a++;                // レジスタ渡しパラメタに代入
      do {
          c=f2(&a);       // レジスタ渡しパラメタのアドレスを参照
          if(c) er();
          else return;
      } while(c);
  }
[出力コード]
_f:
  MOV.L   R13,@-R15
  MOV.L   R14,@-R15
  STS.L   PR,@-R15
  ADD     #-4,R15
  MOV     R4,R0
  ADD     #1,R0
  MOV.B   R0,@(3,R15)  ; ※
  MOV.L   R4,@R15      ; ※で設定したaの値を上書き
  MOV.L   L14+2,R13    ; _f2
             :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
レジスタ渡しパラメタが存在する。
(c)
当該パラメタが関数内でアドレス参照される。
(d)
当該パラメタのアドレス参照より前に、当該パラメタへの代入がある。
(e)
スケジューリング最適化により、レジスタパラメタのスタックへのコピー命令が当該パラメタへの代入命令より前に移動される。

2. #pragma global_register指定レジスタの代入削除不正

現象:

#pragma global_register指定したレジスタの代入を不正に削除する場合がある。

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
#pragma global_registerを指定している。
(b)
optimize=1 を指定している。
(c)
(a)で指定した変数が複合代入式などで1つの式で定義・使用される。
[例]
 #pragma global_register (a=R14)
      :
 a += b;  // もしくは a=a+b;


3. 32bitビットフィールド比較不正

現象:

32bitビットフィールドを0と比較した場合、比較前に不正な0とのANDが生成され、判定結果が常に真(または偽)となる場合がある。

[例]
  struct ST {
      unsigned int b: 32;
  };

  void f(struct ST *x) {
      if (x->b) {
          :
      }
  }
[出力コード]
         :
  MOV.L   @R4,R0
  AND     #0,R0     ; 0とANDをとる
  TST     R0,R0     ; 必ず真となる
  BF      L12       ; L12への分岐は起きない
         :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
構造体に32bitビットフィールドメンバが存在する。
(b)
当該メンバと0の比較(==もしくは!=)を行っている。

4. trapa_svc使用時のスタック移動不正

現象:

pic=1を指定してコンパイルした場合、組み込み関数trapa_svcを使用している関数のアドレスロード時に、不当にスタック移動命令を出力する場合がある。

[例]
  #include <machine.h>
  extern char *b(void (*yyy)(char));

  void y(char c) {
      trapa_svc(160, 10, c);
  }

  char *a(void) {
      return b(y);
  }
[出力コード]
_a:
  MOV.L   L14,R4    ; _y-L12
  MOVA    L12,R0
  ADD     R0,R4
L12:
  MOV.L   @R15+,R0  ; 不要なスタック移動命令
  MOV.L   L14+4,R2  ; _b-L13
  MOVA    L13,R0
  ADD     R0,R2
L13:
  JMP     @R2
  MOV.L   @R15+,R0  ; 不要なスタック移動命令

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
cpu=sh1以外を指定している。
(b)
pic=1を指定している。
(c)
組み込み関数trapa_svcを使用している。
(d)
trapa_svcを使用している関数のアドレスロード位置が、trapa_svcの呼び出し位置より後に出現する。

5. R0-R7へのコピー命令の不当移動

現象:

最適化により、R0-R7へのコピー命令もしくは拡張命令が関数呼び出しを超えて不正に移動し、CMP/EQ(TST)の結果が不正となる場合がある。

[例]
          :
  MOV.L   @R4,R2
  MOV     R5,R14
  MOV     #1,R5       ; H'00000001
  ADD     #24,R2
  MOV.L   @R2,R6
  EXTU.W  R14,R1      ; R1の定義がJSRを越えて移動
  MOV.L   @(8,R2),R7
  JSR     @R7         ; 呼び出し先でR1を破壊する場合あり
  ADD     R6,R4
  MOV     #4,R2       ; H'00000004
  CMP/EQ  R2,R1       ; 破壊されたR1の値を使って比較
  EXTU.W  R0,R0
  BF      L16
          :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定している。
(b)
関数内に条件分岐と、関数呼び出しがある。
(c)
以下最適化を実施。
  [最適化前]
     MOV   R0, Rn     ; またはEXTU  R0,Rn
           :
     MOV   Rx, R0     ; またはEXTU  Rx,R0
     TST   #imm, R0   ; またはCMP/EQ  #imm,R0
     MOV   Rn, R0     ; または EXTU  Rn,R0
  [最適化後]
     MOV   Rx, Rm     ; またはEXTU  Rx,Rm
     MOV   #imm, Ry
     TST   Ry, Rm     ; またはCMP/EQ  Ry,Rm
	 

(d)
(c)の最適化で、RmレジスタがR0-R7で当該関数で他に使用されていない。
(e)
(c)の最適化で生成したMOV Rx,Rm(または EXTU)命令が他の最適化により、関数呼び出しを超えて移動される。

1. R0レジスタの不正破壊

現象:

スタック渡しのパラメタがある時、R0を不正に書き換える場合がある。

[例]
  short func1(short a0, int *a1, int a2, short a3, short a4,
                 short a5, short a6, int a7, int a8, int a9);
  void func0(short a0, int *a1, int a2, short a3, short a4,
                 short a5, short a6)
  {
                  :
      r1=func1(0,a1,0,0,0,0,0,0,0,0);
      if((r1>0)&&(r1!=1)) {
          func1(a0,a1,0,a3,a4,a5,a6,0,0,0);
      }
                  :
  }
[出力コード]
               :
  MOV.L   R0,@(32,R15)   ; R0を@(32,R15)に退避
  MOV     R8,R5
  MOV     #66,R0         ; R0を破壊
  MOV.W   @(R0,R15),R3
  MOV     R9,R6
  MOV     #70,R0         ; R0を破壊
  MOV.W   @(R0,R15),R1
  MOV     R0,R4          ; MOV.L @(32,R15),R4をMOV R0,R4に置換えコード不正
  MOV.L   R3,@(4,R15)
  MOV.L   R1,@(8,R15)
  MOV.L   R9,@(12,R15)
  MOV.L   R9,@(16,R15)
  BSR     _func1
  MOV.L   R9,@(20,R15)
               :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
当該関数にスタック渡しのパラメタが存在する。

2. BRA命令飛び先不正

現象:

無条件分岐を含むプログラムにおいて、分岐がBRA命令で、かつ飛び先までの距離が4094バイトの時、飛び先が不正になるコードを生成する場合がある。

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
code=machinecodeを指定している。またはcodeオプションを指定していない。
(b)
BRA命令から飛び先までの距離が4094バイトである。

3. PRレジスタ退避・回復命令の不当削除

現象:

次のソースをspeedオプションでコンパイル時、不当にPRレジスタの退避・回復命令を削除し、実行時に無限ループする場合がある。

[例]
  int x;
  extern void f1();
  extern void f2();
  void f() {
      if (x == 2){
          f1();       // then節が関数呼び出しで終わっている
      }
      f2();           // 関数の最後の処理が関数呼び出し
      return;
  }
[出力コード]
  _f:
        MOV.L    L14,R6    ; _x
        MOV.L    @R6,R0
        CMP/EQ   #2,R0
        BT       L11
  L12:
        MOV.L    L14+4,R2  ; _f2
        JMP      @R2       ; 関数の最後が関数呼び出しのため、JMP命令に
        NOP                ; 変換し、RTS を削除
  L11:
        MOV.L    L14+8,R2  ; _f1
        JSR      @R2       ; ここに関数呼び出し命令があるにも関わらず
        NOP                ; PRレジスタの退避回復が行われていない
        BRA      L12
        NOP

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
speedオプションを指定している。
(b)
関数の最後の処理が関数呼び出しである。
(c)
(b)の関数呼び出しの直前にif-then節があり、then節の最後が関数呼び出しである。
(d)
(b)の関数呼び出しがインライン展開されない。

4. Tビットの不当参照

現象:

以下のケースにおいて、不当に条件分岐を行なう場合がある。

  • C++プログラムで、関数内のローカルブロック内で宣言されたデストラクタ呼び出しのあるクラスを記述した時。
  • 次例のソースのCコンパイルした時。
[例]
  #include <machine.h>
  extern void f();
  int a;
  void func() {
      int b;
      b = (a == 0);    // 比較結果を変数に格納(A)
      f();             // 関数呼び出しまたは組み込み関数set_cr()
      if(b) {         //(a)の変数を0/1比較
          a=1;
      }
  }
[出力コード]
  _func:
        STS.L    PR,@-R15
        MOV.L    L13,R6    ; _a
        MOV.L    @R6,R2
        TST      R2,R2     ; 比較結果をTビットに格納
        MOV.L    L13+4,R2  ; _f
        JSR      @R2       ; 関数呼び出し先でTビットが
        NOP                ; 破壊される可能性あり
        BF       L12       ; Tビットを参照し分岐
        MOV.L    L13,R6    ; _a
        MOV      #1,R2
        MOV.L    R2,@R6
  L12:
        LDS.L    @R15+,PR
        RTS
        NOP

発生条件:

次の(a)〜(c)または(d)〜(e)の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
Cプログラムで比較結果を変数に格納している。
(c)
(b)の変数を関数呼び出しまたは組み込み関数set_cr()使用後に0/1比較している。
または
(d)
optimize=1を指定する。
(e)
C++プログラムで関数内のローカルブロック内で宣言されたデストラクタ呼び出しのあるクラスを記述している。

5. 定数値の不当共通化

現象:

次のソースをoptimize=1でコンパイル時、定数値を不当に共通化し、参照時の値が実際と異なる場合がある。

[例]
  #define a (*(volatile unsigned short *)0x400)
  #define b (*(volatile unsigned short *)0x4000)
  #define c (*(volatile unsigned short *)0x402)
  int d;

  void func() {
      a = 0x8000;      /* (A)  */
      b = 0x8000;      /* (A') */
      d = c + 0x8000;  /* (B)  */
  }
[出力コード]
  _func:
        MOV.W    L15,R6      ; H'8000  R6に0xFFFF8000を設定
        MOV      #4,R5
        MOV      #64,R2
        SHLL8    R5
        SHLL8    R2
        MOV.W    R6,@R5      ; (A)  aに0x8000を設定
        MOV.W    R6,@R2      ; (A') bに0x8000を設定
        MOV.W    @(2,R5),R0
        EXTU.W   R0,R2
        ADD      R6,R2       ; R2にc+0xFFFF8000の結果を設定
        MOV      R2,R6
        MOV.L    L15+4,R2    ; _d
        RTS
        MOV.L    R6,@R2      ;(B) dにc+0xFFFF8000の結果を格納しNG

発生条件:

次の条件を全て満たす時、発生する。

(a)
optimize=1を指定する。
(b)
関数内で128〜255もしくは32768〜65535の範囲の同じ定数値を複数回使用している。
(c)
(b)の定数値が無符号で、異なるサイズで使用される。上例では(A)(A')で2バイト、(B)で4バイト。

6. リテラルプール出力位置不正

現象:

align16オプションを指定してコンパイルした場合、リテラルプールの参照が届かなくなる場合がある。

  • code=machinecodeを指定した場合
    goptimizeオプションを指定時はリンク時内部エラー、指定しない場合はオブジェクト不正になる場合がある。
  • code=asmcodeを指定した場合
    アセンブル時エラーになる場合がある。
 [例]
              :
        MOV.L     L154+2,R2   ; L158    リテラル(A)を参照(1)
        MOV.L     R2,@R15
        MOV.L     L154+6,R2   ; _printf リテラル(B)を参照(2)
        JSR       @R2
        NOP
              :
        .ALIGN    16
  L86:
        ADD       #1,R2
        BRA       L153        ; 無条件分岐を生成しリテラルを出力
        MOV.L     R2,@R4
  L154:
        .RES.W    1
        .DATA.L   L158        ;(A) (1)からdispが届かない位置にリテラル出力
        .DATA.L   _printf     ;(B) (2)からdispが届かない位置にリテラル出力
        .ALIGN    16
  L153:
              :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
align16オプションを指定する。
(b)
無条件分岐命令を生成してリテラルプールを出力する。

7. 遅延スロットへの不当命令移動

現象:

optimize=1を指定してコンパイルした場合、不当に遅延スロットに命令を移動し、レジスタを破壊する場合がある。

[移動前]
          :
  SHLL        R2
  MOV         R2,R0
  MOVA        L88,R0
  BRA         L144
  NOP
          :
[移動後]
  SHLL        R2
                          ; 遅延スロットに移動
  MOVA        L88,R0      ; R0に値を設定
  BRA         L144
  MOV         R2,R0       ; MOVAで設定したR0を破壊
                :

発生条件:

同一レジスタに値を設定するコードが連続する時、発生する可能性がある。

8. GBR相対論理演算生成時のoffset不正

現象:

optimize=1を指定してコンパイルし、かつ1バイト構造体メンバの論理演算をGBR相対で行なうコードを生成した場合、そのoffset値が不正になる場合がある。

[例]
  struct {
      int a;
      unsigned char b;
  } ST;
  char c;
  void f() {
      ST.b |= 1;
      c &= 1;
  }
[出力コード]
  _f:
        STC      GBR,@-R15
        MOV      #0,R0         ; H'00000000
        LDC      R0,GBR
        MOV.L    L11+2,R0      ; H'00000008+_ST  <- 実際は(ST+4)
        OR.B     #1,@(R0,GBR)
        MOV.L    L11+6,R0      ; _c
        AND.B    #1,@(R0,GBR)
        RTS
        LDC      @R15+,GBR

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
gbr=userを指定しかつ#pragma gbr_base/gbr_base1を使用している、またはgbr=autoを指定しかつmapオプションを指定していない。
(c)
サイズが1のメンバを含むグローバル構造体が存在する。
(d)
サイズ1の構造体メンバは構造体先頭でない。
(e)
(c)のメンバが関数内で論理演算で使用されている。
(f)
(c)のメンバは論理演算以外では使用されていない。
(g)
関数内に論理演算のみで使用される外部変数が(c)以外に存在する。

9. SWAP命令直後の不当ゼロ拡張

現象:

optimize=1を指定してコンパイルした場合、組み込み関数swapb、swapw、end_cnvlの結果代入先にポインタを指定すると、不当にゼロ拡張を行う場合がある。

 [例]
  #include <machine.h>
  unsigned short *a,*b;
  void func() {
      *b=swapb(*a);
  }
[出力コード]
  _func:
        MOV.L    L13+2,R2   ; _a
        MOV.L    L13+6,R5   ; _b
        MOV.L    @R2,R6
        MOV.W    @R6,R2
        SWAP.B   R2,R6
        MOV.L    @R5,R2
        EXTU.B   R6,R6      ; SWAP結果を不正にゼロ拡張
        RTS
        MOV.W    R6,@R2

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
組み込み関数swapb、swapw、end_cnvlを使用している。
(c)
(b)の組み込み関数の結果代入先がポインタである。

10. ビットフィールドデータ出力不正

現象:

無名ビットフィールhを含む構造体に初期値を設定した場合、その初期値が不正になる場合がある。

[例]
  struct st {
      short a:4;
      short b;
      short :12;    // 無名ビットフィールド
      short c:4;
  } ST={1,1,3};
[出力コード]
  _ST:
        .DATA.W     H'1000
        .DATA.W     H'0001
        .DATAB.B    1,0       ; 正しくは
        .DATA.W     H'0300    ; .DATA.W H'0003

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
構造体の中にビットフィールド、無名ビットフィールド、ビットフィールドでないメンバが混在し、以下の順番で定義されている。
    struct A{
       :
    ビットフィールド
       :
    ビットフィールドでないメンバ
    無名ビットフィールド    //(A)
    ビットフィールド      //(B)
       :
    };

(b)
(A)(B)のメンバの型サイズが2バイト以上である。
(c)
(A)の無名ビットフィールドのサイズが8ビット以上である。
(d)
(A)(B)のビットフィールドのサイズの合計が以下の通りである。
・(A)(B)の型サイズがともに2バイトの場合 :16ビット以下
・(A)(B)の型サイズがともに4バイトの場合 :32ビット以下
(e)
構造体の変数が定義時に初期値設定されている。

11. ループ展開不正

現象:

speedオプションまたはloopオプション指定時、ループ展開最適化でループ判定式を不当に置きかえる場合がある。

[例]
  int a[100];
  void main(int n) {
      int i;
      for (i=0; i<n; i++) {
          a[i] = 0;
      }
  }
[出力コード]
  _main:
        MOV      R4,R7
        ADD      #-1,R4    ; R4が0x80000000の場合、アンダフローとなり
                           ; R4は0x7FFFFFFFとなる
        MOV      R4,R6
        CMP/PL   R4        ; 比較結果が実際の結果と逆になる
        MOV      #0,R4
        BF       L12
        ADD      #-1,R6
             :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

  1. speedオプションまたはloopオプションを指定している。
  2. 関数内でループ文を使用している。
  3. ループ上限値が以下の通りである。
    1. 制御変数がインクリメントされていくループの場合(i += step)
      ループ上限値が0x80000000〜0x80000000+step-1の範囲である。
    2. 制御変数がデクリメントされていくループの場合(i -= step)
      ループ上限値が0x7FFFFFFF〜0x7FFFFFFF-step+1の範囲である。

上限値が変数の場合、変数にa.またはb.を満たす値が設定されている場合に動作不正になる。

12. 構造体、配列引数の不当参照

現象:

関数の引数に構造体/共用体または配列を使用した場合、そのメンバや配列要素を参照する時に、不正なアドレスを参照する場合がある。

[例]
  typedef  struct{
      int A[10];
      double B;
      char F[20];
  } ST;
  extern ST f(ST a,ST b);
  ST S;
  extern int X;
  void func(ST a,ST b) {
      ST t;
      if (a.B!=f(S,t).B){
          X++;
      }
      if (a.B!=b.B){
          X++;
      }
  }
[出力コード]
                :
  L12:
        MOV      R15,R2
        MOV.W    L15+2,R0   ; H'014C
        ADD      R0,R2
        MOV      R15,R0
        MOV.W    L15+4,R0   ; H'0190  R0を不正に破壊
        ADD      R0,R0
        MOV.L    @R2,R4
        MOV.L    @(4,R2),R7
        MOV      R0,R2
        MOV.L    @R2,R6     ; b.Bでない場所をアクセス
                :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
引数が構造体/共用体または配列である関数が存在する。
(b)
該当関数内で引数の構造体/共用体メンバまたは配列要素を参照している。

13. JMP命令の不当削除

現象:

speedオプションを指定してコンパイルした場合、JMP命令を不当に削除する場合がある。

[例]
  void f(){
      int i,j=0;
      for (i=0; i<10; i++)
          if (j%2) j++;
      sub();
  }

  void sub() {
      :
  }
[出力コード]
  _f:
                :
  L20:
        ADD         #-1,R5
        TST         R5,R5
        BF          L11
        MOV.L       L23,R2    ; _sub  以下3命令が削除される
        JMP         @R2       ;                |
        NOP                   ;                V
  L18:
        MOV         R6,R0
        AND         #1,R0
        BRA         L16
        MOV         R0,R2
  L13:
        MOV         R6,R0
        AND         #1,R0
        BRA         L14
        MOV         R0,R2
  _sub:
                :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
speedオプションを指定する。
(b)
該当関数の最後の処理が関数呼び出しである。
(c)
(b)の関数呼び出しがインライン展開されない。
(d)
(b)の関数呼び出し先が該当関数の直下にある。
(e)
該当関数内でループ文や条件文を使用している。

14. ループ判定式不正

現象:

次のCソースをoptimize=1でコンパイル時、ループ判定式を不当に置きかえる場合がある。

[例]
  void f1() {
      int  i;
      for (i=-1; i<INT_MAX; i++) {
          a[i] = 0;
      }
  }
[出力コード]
  _f1: 
        MOV.L    L13,R2    ; _a
        MOV      #-4,R6    ; H'FFFFFFFC
        MOV      R6,R5
        MOV      #0,R4     ; H'00000000
        ADD      #-4,R2
  L11:
        ADD      #4,R6
        MOV.L    R4,@R2
        CMP/GE   R5,R6     ; H'FFFFFFFCと比較
        ADD      #4,R2
        BF       L11
        RTS
        NOP

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定している。
(b)
関数内でループ文を使用している。
(c)
ループ上限値 - ループ下限値がオーバフローする。
例えば、for (i=-1; i < 0x7FFFFFFF; i++)

1. 副作用のある式を含むif文の不正削除

現象:

if文の削除最適化で副作用のある条件式が削除される場合がある。

[例]
 extern int sub();
  main() {
    int i=0;
    if(sub()) {    /* if文内の関数sub()の呼び出しが不正に削除される */
      i=10;        /* ローカル変数iはこの後どこでも使用されないので */
    }              /* 最適化によりi=10が削除される                  */
  }

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
if文のthen節、else節とも空文である(最適化により空文になる場合を含む)。

2. スタックアクセス不正

現象:

スタックに対するロード・ストアが不正になる場合がある。

[例]
        :
  MOV         R15,R2
  ADD         #72,R2
  MOV         #104,R0    ; H'00000068
  MOV.L       R2,@(R0,R15)              <- (SP+72)にデータを保存
  MOV         R15,R3
  ADD         #92,R3                    <- 正しくは#72
  MOV.L       @R3,R6                    <- (SP+92)からデータ取り出し NG
  MOV.L       L37+60,R4  ; L52
  BSR         _func
  MOV         #52,R5     ; H'00000034
        :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
スタックへのアクセスがある。
(b)
1関数の最適化対象範囲が複数に分割されている(分割される条件は、サイズ、変数、関数呼び出しの数等に起因する)。

3. #pragma interrupt関数のレジスタ退避・回復不正

現象:

#pragma interrupt関数から#pragma inline_asm指定関数を呼び出し時、#pragma inline_asm関数内で使用したレジスタが破壊されることがある。

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
#pragma interrupt関数から#pragma inline_asm指定関数の呼び出しがある。
(b)
当該#pragma interrupt関数内に他の関数呼び出しがない。

4. MACレジスタ退避・回復不正

現象:

macsave=1オプション指定時、#pragma regsave指定関数でMACレジスタが退避・回復されない。

発生条件:

次の条件を全て満たす時、発生する。

(a)
macsave=1を指定する。
(b)
#pragma regsaveを指定する。

5. 二重ループ内式のオブジェクト不正

現象:

二重ループのある式でspeedオプションまたはloopオプションを指定すると、コード不正になる場合がある。

[例]
        :
  for(...)
      for(...)
         x = x + i;    /* xの値が不正になることがある */
        :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
speedオプションまたはloopオプションを指定する。
(c)
二重ループがあり、ループの内側にx=x+a;のように左辺と同じ変数を右辺で使用している。

6. 拡張命令の削除不正

現象:

不要なロード・ストア命令削除または複数ロード命令削除で不正にEXTS命令を削除してしまう場合がある。

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
代入先(または複数ロード命令の2回目以降)サイズ < 参照元サイズ <= 4byteである。

7. switch文展開不正

現象:

switch文がテーブル方式展開される場合、遅延スロットにケーステーブルのリテラルデータが入り、コード不正になる場合がある。

[例]
          SHLL        R6
          MOVA        L204,R0
          MOV.W       @(R0,R6),R0
          BRAF        R0
          NOP
  L203:
          .RES.W      1
          .DATA.L     _fl
          .DATA.L     _f2
          .DATA.L     _f3
          .DATA.L     _f4
          .DATA.L     _f5
          .DATA.L     _f6
  L204:
          .DATA.W     L143-L203
          .DATA.W     L143-L203
          BRA         L180
          .DATA.W     L143-L203      ←遅延スロットに入る

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
switch文があり、分岐方式がテーブル展開である。

8. 定数演算不正

現象:

定数演算でコード不正になる場合がある。

 [例]
  void f() {
      sl = 0x80000001L;
              :
      if (*scp16==(char)(0x80000001L)) 
              :       /* 0x80000001を1byteに型変換した結果(1)と比較 */
                      /* すべきところ、0x80000001と比較             */
  }

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
同一関数内に、サイズの異なる部分的に等しい定数式がある。

9. ポインタ指示先への設定不正

現象:

ポインタの指示先への値の設定が正しく行われない場合がある。

 [例]
  int *gp;
  void g(int *p) {
      gp = p;
  }
  int f(int *q) {
      static int i, *p = &i;
      g(p);        /* gpがiを指すように設定される */
          :
      i = 1;
      *gp = 2;     /* *gpとiが同一である可能性があることが認識できていない */
          :
      return i;    /* i = 1が定数伝播され、2ではなく1が返される */
  }

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
静的変数および外部変数の初期値にアドレス参照式を指定、または&演算子を用いない配列のアドレス参照がある。

10. #pragma interrupt関数のFPSCR退避・回復不正

現象:

#pragma interrupt指定関数でFPSCRが退避・回復されない。

発生条件:

次の条件を全て満たす時、発生する。

(a)
cpu=sh2e/sh4を指定する。
(b)
#pragma interrupt関数内に関数呼び出しがない。
(c)
#pragma interrupt関数内にオペランドにFPSCRのあるコードが無い。
(d)
#pragma interrupt関数内にFPU演算(FABS,FSQRT,FNEG,FCNVDS,FCNVSD,FLOAT,FTRC,FADD,FSUB,FMUL,FDIV,
FCMP/EQ,FCMP/GT,FMAC)がある。

11. 組込み関数の移動不正

現象:

以下の組み込み関数を跨いで命令が移動することがある。
set_cr, get_cr, set_imask, get_imask, set_vbr, get_vbr, trapa, trapa_svc, prefetch

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
以下の組み込み関数を使用。
    set_cr, get_cr, set_imask, get_imask, set_vbr, get_vbr, trapa, trapa_svc, prefetch

12. 乗算結果の拡張命令削除不正

現象:

乗算結果を1/2byteに代入時、不正に拡張命令が削除される場合がある。

[例]
  unsigned short eus=32768,eus2=32769;
  void func () {
      unsigned short aus;
      aus= (eus--) * (eus2--);        /* 乗算結果のゼロ拡張が不正に削除 */
      if (aus!=(unsigned short)((1+eus)*(1+eus2))){
              :               /* (1+eus)*(1+eus2)の結果はゼロ拡張される */
  }

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
乗算結果を1byteまたは2byteに代入する。

13. volatileへのキャスト不正

現象:

volatileへのポインタに型変換を指定時、ループ内の式のvolatile指定が無効になる場合がある。

[例]
  #define ADDRESS1 0xf0000000
  #define ADDRESS2 0xf0000004
  #define BIT0     0x0001
  void shc_test(void) {
      unsigned short dataW,read_val;
      read_val = *((unsigned short*)ADDRESS1);
      while(1) {
          /* volatileが無効になりループ外に移動 */
          dataW = *((volatile unsigned short*)ADDRESS2);

          if(dataW & BIT0) {
              /* volatileが無効になりループ外に移動 */
              *((volatile unsigned short*)ADDRESS2) &= ~BIT0;
              break;
          }
      }
  }

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
指示先がvolatileであるポインタキャストが存在する。
(c)
そのポインタの間接参照式がループ内で使用されている。

14. GBR相対論理演算化不正

現象:

外部変数の複合論理演算指定時に、コード不正になる場合がある。

[例]
  unsigned char guc1=255;
  void func001() {
      if (guc1 & 254){    /* guc1をロード(A)                               */
          guc1 &= 253;    /* AND.B #253,@(R0,GBR) メモリ上のguc1を書き換え */
      }
      if (guc1==253) ok(1);   
                         /* guc1を再ロードせず、(A)をそのまま使用       */
      else           ng(1);
  }

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
gbr=autoを指定している。またはgbr=user指定時#pragma global_base/#pragma global_base1を使用している。
(c)
外部変数を複合論理演算式(&=, |=, ^=)で使用している。
(d)
複合論理演算式を「op.B #xx,@(R0,GBR)」(op:AND/OR/XOR)でコード生成している。

15. #pragma global_register指定時の拡張命令削除不正

現象:

1/2byte変数を#pragma global_register指定すると、不正にEXTS/EXTU命令が削除される場合がある。

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
(unsigned) char/short型変数を#pragma global_registerに指定している。

16. 構造体・共用体配列メンバを含む構造体・共用体使用時のオブジェクト不正

現象:

構造体・共用体に構造体・共用体配列メンバがある場合にオブジェクト不正になる場合がある。

[例]
  void func() {
      struct tag {
          union {
              short  u11;
              char   u12[2];
          } u1[2];
      } st= {{{0x1234},{0x5678}}};
      char ans1;
      ans1=st.u1[1].u12[1];     /* st.u1[1].u12[1]設定前にans1をコピー(A) */
      printf("%x\n",ans1);      /*(A)のans1をそのまま使用                */
      if(ans1 == 0x78)          /*(A)のans1をそのまま使用                */
                :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
構造体・共用体に構造体・共用体配列メンバが存在する。
(c)
上記の領域を配列メンバとそれ以外の直接参照式(構造体全体を表す式や共用体の別メンバ)の両方で参照している。

17. goptimize指定時のoptlnk最適化不正

現象:

goptimize指定時に、レジスタ退避・回復コードが遅延スロットに移動した場合、optlnkで不正に退避・回復コードを削除する場合がある。

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
goptimizeを指定する。
(c)
optlnkでoptimize=registerを指定する。
(d)
遅延スロットにレジスタ退避・回復コードがある(MOV.L @R15+,Rn/MOV.L Rn,@-R15など)。

18. 組込み関数nopの遅延スロット移動抑止

現象:

組込み関数nopを引用した場合、不当に遅延スロットにNOP命令を移動し指定位置に設定できない。

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
組込み関数nopを使用する。

19. 符号付き整数型定数値のキャスト不正

現象:

定数値をcharまたはshort型に明示的型変換指定をした場合、コード不正になる場合がある。

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
定数値を符号付きのchar, short型に型変換している。
(c)
その定数値は型変換後、負の値となる。

20. 副作用のある実引数の演算不正

現象:

関数パラメタにvolatile変数に対する後置++/--式を記述した時、呼び出し関数内で当該変数が後置++/--前の値になる場合がある。

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
optimize=1を指定する。
(b)
関数のパラメタがvolatileで変数である。
(c)
当該パラメタが副作用(後置++/--)式である。

21. rtnext指定時の返却値の符号/ゼロ拡張不正

現象:

rtnextオプション指定時、1/2byteのリターン値が不正になる場合がある。

[例]
  int a;
  unsigned char func(){
      return a;        /* aの上位3byteは不定値が返る */
  }

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
rtnextを指定している。
(b)
関数のリターン値が1byteまたは2byteである。
(c)
関数の原型宣言を行っていない。

22. 無名ビットフィールドデータ配置不正

現象:

endian=little指定時、構造体メンバに無名ビットフィールドがあるとデータ配置が不正となる場合がある。

発生条件:

次の(a)-(d)もしくは(e)-(h)の条件を全て満たす時、発生する可能性がある。

(a)
endian=littleを指定する。
(b)
構造体メンバにビット幅が8以上の無名ビットフィールドがある。
(c)
その無名ビットフィールドは構造体先頭ではない。
(d)
構造体変数の定義で初期値が設定されている。
(e)
endian=littleを指定する。
(f)
構造体に配列メンバがあり、その次がビットフィールドメンバである。
(g)
配列メンバとビットフィールドメンバの間に境界調整のギャップが入る。
(h)
構造体変数の定義で初期値が設定されている。

23. volatile指定変数参照削除の抑止

現象:

volatile宣言された変数について、次のような式を記述した場合、変数の参照を保証するように変更した。
  a &= 0;

24. デバッグ情報不正

現象:

ローカル配列、構造体・共用体(スタックに領域を確保した時)のデバッグ情報が出力されなかった。

25. インターナルエラー

現象:

次の条件でインターナルエラーが発生する場合がある。

(a)
code=machinecode指定時に、コンパイル実行するドライブのルートディレクトリにファイルを生成する権限がない場合(Windows版のみ)。
(b)
無限ループがあり関数出口に到達する制御フローのないプログラムをコンパイルした場合。

1. 符号拡張/ゼロ拡張不正

現象:

関数内に結果が同じ整数型の部分式が存在する場合、その結果の下位2バイトまたは1バイトを不正に符号拡張/ゼロ拡張する場合がある。

[例]
 short sub(long);
 short a,b,c,d;

 void f()
 {
     if(d) {
         b = sub((long)a * (long)b * (long)c / 0x4000);
              /* ^^^^^^^^^^^^^^^^^ */
     } else {
         b = sub((long)a * (long)b * / 0x80);
              /* ^^^^^^^^^^^^^^^^^ */
     }
 }

発生条件:

次の条件を全て満たす時、発生する。

(a)
関数内に結果が同じである共通な部分式がある(上記例の^^^^^)。
(b)
その部分式の変数の値が2バイトまたは1バイト以下であることが保証できる。
(上記例ではshort型の変数を型変換しているので、値としてはshort型の範囲内であることが保証できる)

2. レジスタ破壊不正

現象:

optimize=1 指定時、使用中のレジスタを不正に破壊する場合がある。

発生条件:

次の条件を全て満たす時、発生する。

(a)
optimize=1を指定する。
(b)
関数内に分岐先(ループ文、return文も含む)が同一になる複数の基本ブロック(分岐、ラベルのない命令列)が存在する。
(c)
(b)の複数基本ブロックの中に同じ変数への代入文がある。

3. 1ビットフィールドメンバ定数代入不正

現象:

同じ構造体の1ビットフィールドメンバへの定数代入文が連続し、かつ同じビットフィールドメンバへの代入文が複数存在する時、インターナルエラーもしくはオブジェクト不正になる場合がある。

[例]
 struct {
     unsigned char b1:1;
     unsigned char b2:1;
 } ST;

 /* インターナルエラー */
 void f() {
     ST.b1=1;
     ST.b2=0;
     ST.b2=0;
 }

 /* オブジェクト不正 */
 void g() {
     ST.b1=1;
     ST.b2=1;
     ST.b2=0;
 }

発生条件:

次の条件を全て満たす時、発生する。

(a)
同じ構造体の1ビットフィールドメンバへの定数代入文が連続している。
(b)
同じビットフィールドメンバに同じ値を代入している(インターナルエラー)、もしくはビットフィールドメンバに0以外の定数を代入し、直後に同じビットフィールドメンバに0を代入している。

4. レジスタ退避・回復不正

現象:

割り込み関数内で、R0,R1,R3が使用されているにも関わらず退避・回復されない場合がある。

発生条件:

次の条件を全て満たす時、発生する。

(a)
当該関数が割り込み関数である。
(b)
当該関数内で関数呼び出しがない(実行時ルーチンを含む)。
(c)
当該関数内に以下のいずれかを含む。
  ・switch文
  ・#pragma inline_asm指定関数(サイズが255以上もしくは指定なし)の呼び出し
  ・関数内分岐が8bitで届かない場合

5. R0レジスタの不正破壊

現象:

optimize=1指定時、使用中のレジスタR0を不正に破壊してしまう場合がある。

[例]
  ret = func(a,b,c);
  func2(d,e,f,g,h,ret,i,j);
             :

 [出力コード]
  JSR         @R2
  MOV.L       @(R0,R15),R8
  MOV.B       R0,@(28,GBR)         ; <- func()の戻り値をretに格納
  EXTU.B      R14,R6
  MOV.W       L2448+8,R0 ; H'0087  ; <- R0を破壊
  MOV.B       @(R0,R15),R5
  MOV         R10,R4
  MOV.W       L2448+4,R0 ; H'0083  ; <- R0にH'0083を設定
  MOV.B       @(R0,R15),R7
  STC         GBR,R2
  ADD         #18,R2
  MOV.L       R9,@R15
  MOV.L       R2,@(8,R15)
  EXTU.B      R5,R5
  STC         GBR,R2
  ADD         #56,R2
  MOV.B       R0,@(7,R15)          ; <- R0(H'0083)をfunc2()の第6引数
  EXTU.B      R7,R7                ;    として設定
  BSR         _func2
  MOV.L       R2,@(12,R15)
            :

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
-optimize=1を指定する。
(b)
スタック渡しの仮引数が存在する。

6. switch文条件式中の構造体メンバ参照不正

現象:

switch文の条件式にネストした構造体のメンバ参照を記述した場合、メンバへのオフセットが不正となる場合がある。

[例]
  struct ST1 {
      int B0;
      int B1;
      int B2;
      int B3;
  };

  struct ST {
      struct ST1 A0;
      struct ST1 A1;
      struct ST1 A2;
      struct ST1 A3;
  };

  int x;

  test(struct ST *s) {
      switch ((&s->A1)->B2) {
      case 1:
          x=1;
      }
  }

 [出力コード]
      MOV.L       @(8,R4),R0 ; <- @(24,R4)が正しい
      CMP/EQ      #1,R0
      BF          L15
      MOV.L       L17+2,R6   ; _x
      MOV         #1,R2      ; H'00000001
      RTS
      MOV.L       R2,@R6
  L15:
      RTS
      NOP

発生条件:

次の条件を全て満たす時、発生する可能性がある。

(a)
ネストした構造体のメンバ参照がある。
(b)
そのメンバの参照を、'.'ではなく、(&member)->という形で行っている。
(c)
その構造体参照式が、switch文の条件式にある。

7. デバッグ情報不正

現象:

構造体メンバが関数へのポインタで、その関数の型がプロトタイプ形式で宣言されている場合に、デバッグ情報が不正になる。

発生条件:

次の条件を全て満たす時、発生する。

(a)
C言語で記述されている。
(b)
関数型へのポインタである構造体または共用体のメンバが存在する。

8. インターナルエラー

現象:

次の場合、インターナルエラーが発生する。

(a)
ファイル名(パスを含む)に空白を含む場合。
(b)
mapオプションでファイル名(パスを含む)に日本語を指定した場合。