PowerPCアセンブリー

PowerPCでのアセンブリー入門

最近、アセンブリー言語は、プログラミングの世界であまり知られることがなくなっており、PowerPCのアセンブリーは、なおさら縁遠いものになっています。Hollis BlanchardがPowerPCの世界からアセンブリー言語の概要を紹介するとともに、ia32、ppc、およびppc64の3つのアーキテクチャーの例を比較してくれます。

Hollis Blanchard (hollisb@us.ibm.com), Software Developer, IBM 

Hollis Blanchardは、PowerPCアセンブリーのプログラミングを始めて約6ヵ月になります。彼は、2001年にカーネギー・メロン大学を卒業した後、IBMのLinuxテクノロジー・センターに所属してLinuxなどのPowerPCプロジェクトに加わっています。彼のメール・アドレスはhollis@austin.ibm.com です。



2002年 7月 01日

通常、高級言語のほうが、新鮮味に欠けた繰り返しの多い部分をプログラマーから隠してくれ、プログラマーが目的とすることに集中できるという点で、大きな利点があるのですが、場合によっては、ハードウェアを直接扱うコードや性能的に非常に厳しいコードが必要な場合など、低レベルの言語を使用しなければならない場合もあります。アセンブリー言語は、ハードウェアに最も近いプログラミング言語であり、そのような状況では、当然最後の手段としてアセンブリー言語が使用されます。

本稿では、コンピューター設計の基本的なこと (たとえば、プロセッサーはレジスターを備え、メモリーをアクセスできるといったこと) やオペレーティング・システムの基本的なこと (システム・コール、例外、プロセス・スタック) は理解しているものとして話を進めます。本稿は、すでにia32アセンブリーのことがわかっていて、さらに知識を広げたいというプログラマーだけでなく、アセンブリーのことをよく知らないPowerPCプログラマーにとっても役に立つことと思います。

PowerPC入門

1993に発表されたPowerPCアーキテクチャー仕様 (PowerPC Architecture Specification) は、32ビットのサブセットを持つ64ビットの仕様です。一般に利用されているPowerPCは、(IBM RS/6000の最新モデルとIBM pSeriesハイエンド・サーバーを例外として) ほとんどが32ビットです。

PowerPCプロセッサーには、Power4などのハイエンド・サーバー用CPUから組み込みCPU市場まで (任天堂のGamecubeはPowerPCを使用している)、幅広い実装バージョンがあります。PowerPCプロセッサーは、性能が高く、消費電力が小さく、熱放散も少ないため、組み込み市場では強力な地歩を確立しています。組み込み用プロセッサーは、シリアル・コントローラーやイーサネット・コントローラーなどのI/Oを搭載すること以外にも、「デスクトップ用CPU」とはずいぶん違うものになることがあります。たとえば、4xxシリーズのPowerPCプロセッサーは、浮動小数点機能を備えていませんし、メモリー管理も、デスクトップ・チップで使われる逆ページ表 (inverted pagetable) ではなく、ソフトウェア制御のTLBを使用します。

PowerPCプロセッサーは、32個の (32ビットまたは64ビットの) GPR (汎用レジスター) を装備する他、PC (プログラム・カウンター: IAR / 命令アドレス・レジスター (Instruction Address Register) とかNIP/ 次命令ポインター (Next Instruction Pointer) ともいう)、LR (リンク・レジスター)、CR (条件レジスター) など、いろいろなレジスターを備えています。PowerPC CPUの中には、64ビットのFPR (浮動小数点レジスター) を32個装備するものもあります。

RISC

PowerPCのアーキテクチャーは、RISC (縮小命令セット・コンピューティング) アーキテクチャーの1種です。したがって、

  • PowerPCは、すべて (64ビット・バージョンも含めて)、固定長32ビットの命令を使用し、
  • PowerPCの処理モデルは、メモリーからデータを読み出し、レジスターを使ってそのデータを操作し、またメモリーに書き戻す、というものになります。メモリーを直接操作する命令は、(ロードとストア以外には) ほとんど存在しません。

アプリケーション・バイナリー・インターフェース (ABI)

理論上は、GPRはどんな用途にでも使用することができます。たとえば、「スタック・ポインター・レジスター」は存在しませんが、任意のレジスターをスタック・ポインターとして使用することができます。実際的には、バイナリーのオブジェクトに、いろいろなコンパイラーや作成済みのアセンブリー・コードとの互換性をもたせるために、一連の約束ごとを定義しておくと便利です。

呼び出し規則 (calling conventions) は、使用するABI (アプリケーション・バイナリー・インターフェース) で決まります。ppc32のLinuxおよびNetBSDは、SVR4 (System V R4) ABIを使用しますが、ppc64のLinuxはAIXに従って、PowerOpen ABIを使用します。ABIは、また、サブルーチンを呼び出す際に、どのレジスターを揮発性 (呼び出し側で保証) あるいは不揮発性 (呼び出される側が保証) とみなすかなど、いろいろなことを規定します。

SVR4のABIで規定されている動作の具体的な例としては、以下のようなものがあります。

  • PowerPCにはGPRがたくさんあるので (ia32の8個に対して32個)、引数は、gpr3 以下のレジスターに入れて渡す。
  • レジスターgpr3 からgpr12 までは、揮発性の (呼び出し側で保証) レジスターとし、これらのレジスターは、(必要な場合) サブルーチンを呼び出す前に保存し、リターンした後に復旧しなければならない。
  • レジスターgpr1 は、スタック・フレーム・ポインターとして使用する。

SVR4の数多くの機能は、PowerOpenのABIと同じであり、その点は、互換性 (interoperability) を確保する上で多いに役立っています。


いつアセンブリーを使うか

"Assembly HOWTO" (リンク先は参考文献を参照) に示されている賛否は、すべて、PowerPCにも当てはまります。

マシン固有のレジスター

場合によっては、高級言語がまったく関知することのないCPUレジスターを扱わなければならないことがあります。とくにオペレーティング・システムを記述する場合に、そういうことが言えます。簡単な例が、みなさんのコードに、それ自身のスタックを割り当てる場合です。PowerPCではr1 をセットする必要があります。コンパイラーはr1 をインクリメントまたはデクリメントするだけですので、アプリケーションが、ハードウェア上で直接実行されるものである場合には、Cのコードを呼び出す前にr1 をセットする必要があります。また、オペレーティング・システムの例外ハンドラーの場合もそうで、上位レベルのコードを安全に呼び出せるようになるまで、レジスターを1個ずつ操作しながら、慎重に状態を保存、復旧する必要があります。

ただし、低レベルのハードウェア機能を使用しなければならない状況に直面した場合でも、アセンブリーでの実装は、できるだけ少なく抑えるべきです。

  • Cのコードは、移植性が高く (portable)、圧倒的な数の開発者によって理解されています。アセンブリー・コード (とくにPowerPCのアセンブリー) は、そうではありません。
  • 多くの場合、高いレベルのコードになるほど、デバッグも、アセンブリーより、はるかに易しくなります。
  • 高いレベルのコードのほうが、その名が示すとおりアセンブリーよりも表現力に富みます。すなわち、少ないコードで (かつ短い時間で)、多くのことを実現できます。

ループとかCの構造体のような高度な構文をアセンブリーで記述することになった場合には、少し再考し、それが別の言語で実現できないか考えてみてください。高級な言語が使用できない部分だけをアセンブリーでコーディングするというのが原則です。

最適化

アセンブリー言語が使用される最も一般的な理由の1つは、遅いプログラムを高速化したいということです。しかし、そのような場合でも、本当に最後の手段としてアセンブリーを考えるべきです。

最適化に関する一般的な助言は、本稿の範囲を越えるものですが、以下に、いくつか端緒となるものを紹介しておきます。

  • プロファイル
    最適化を行うときは、まず、コードのプロファイルをとるべきです。そうすれば、どこが最適化すべき個所 (ホットスポット) なのかがわかる (予期していたところと違うことが多い) だけでなく、高速化を試みた結果、実際に高速化されたのかどうかの証明も得られることになります。ホットスポットがわかれば、(その部分をアセンブリーで書き直す代わりに) まず高級言語のコードを最適化することを検討することもできます。
  • アルゴリズムの最適化
    どんなに無駄のないアセンブリー・コードであっても、n4 のアルゴリズムを使用しているかぎりは、とてつもなく遅いものしか得られません。その他、最初に試してみる必要のある技法には、より適切なデータ構造を使うということもあります。リンク・リストを何度も繰り返し探索しているのであれば、ハッシュ・テーブルや二分木など、そのアプリケーションに適したものを使うことを考えてみてください。

だいたいにおいて、コンパイラーのほうが、アセンブリーで記述したものより、はるかに素晴らしい仕事をしてくれます。高級言語のコードをアセンブリーで書き直そうと考えるよりも、-O3 のような最適化オプションや__inline__ のようなCの指令 (directives) を使用するほうが賢明です。プロセッサーの内部的な動きを判断して、常にすべてのパイプラインを充填された状態にしようとする、命令スケジューリングなどのからくり (tricks) を、コンパイラーは知っています。命令ストリームの観点から見て、必要とされるタイミングよりも早めにロード内容を転送するといったことが行われたりします。これはCPUがメモリー・アクセス待ちにより、パイプラインが停滞することを避けるためです。長年アセンブリーをコーディングしてきた人でもなければ、これは、ほとんど手作業で正しくこなせるような類の仕事ではありません。

モトローラの74xx ("G4") シリーズ・プロセッサーに、Altivecという (あるいはVMXともいう) SIMD (単一命令複数データ: Single Instruction Multiple Data) 型の128ビット・ベクトル・コプロセッサーがあります。とはいうものの、実際には、マシンに固有な1群のレジスターとみなすことができ、最適化のためにだけ使用されるもので、そういう意味で、ここで紹介しておきたいと思います。(Altivecについては、本稿の「マシン固有のレジスター」か「最適化」の2つの部分でとりあげることができたのですが、「最適化」のところで紹介することにしました。) Altivecは、科学計算とかビデオ処理などのアプリケーションで、非常に効果的な使い方ができます。

Altivecは、ある種の演算を非常に高速に実行します。ただし、それには、コストも伴います。Altivecのレジスターとの間で128ビットのロード、ストアを行うには、メモリーでも128ビットの整列 (alignment) が必要となります。さらに悪いことには、Altivecは、非整列アクセス (unaligned access) がなされたとしても、整列例外を発生させません。Altivecは、ただ単に、その整列を実行するだけで、プログラマーが扱うつもりのなかったメモリーをロード、ストアします。

現在のGNUのbinutilsは、Altivecの命令をサポートしていますが、バージョン3.1以前のgccは、Cコードの自動ベクトル化 (vectorization) としても (これは、Forthのような言語では可能なようです)、公開されたCの拡張としても、サポートしていません。AltivecをGNUのツールチェーンといっしょに使用するには、アセンブリーでコーディングを行う必要があります。したがって、Altivecを使うということは、アセンブリーでコーディングを行うことの完璧な理由付けになります。

gcc 3.1は、新しい「ベクトル拡張 (Vector Extensions)」でAltivecをサポートするようになりました (これの詳細については、参考文献にリンクを示してあります)。残念ながら、現在のところgcc 3.1を使っている人はごくわずかであり、PowerPCのLinuxディストリビューションにはgcc 3.1が添付されていません。

もう1つ残念なことに、筆者のところにはG4がないため、Altivec命令の使い方を詳しく解説することができません。Altivecの詳細については、altivec.orgを参照ください (参考文献参照)。


アセンブリーの学び方

アセンブリーを学ぶには (アーキテクチャーにかかわらず)、gccで始めるのが最善です。gcc -O3 -S file.c とすると、gas互換フォーマットのfile.s が生成されます (gas とはGNU Assemblerのこと)。file.s を好みのエディターでオープンすると、Cコードのアセンブリー出力を見ることができます。

たぶん、わからない命令もあることでしょう。それらの命令は、The PowerPC Architecture: A Specification for a New Family of RISC Processors, 第2版やPowerPC Microprocessor Family: The Programming Environments for 32-bit Microprocessors で調べればわかります (これらの文書へのリンクは、参考文献を参照)。ただし、どんな (音声) 言語を学ぶ場合でもそうですが、重要で知っておかなければならない単語がある一方で、コードのもっと重要な機能が理解できるようになるまでは無視しても問題ない単語もあります。重要な命令のよい例が、blr などの分岐関係の命令です。


アセンブリーの例

Hello World -- ia32アセンブリー

リスト1は、Assembly HOWTOのgasの例から直接コピーしてきたもので、残念ながら完全にia32固有なコードです。このコードは、直接的なシステム呼び出しを2回行っています。1つ目は、stdoutへの書き出しを行い、2つ目は (リターン・コード0 を添えて) アプリケーションを終了しています。直接システム呼び出しを行うことは、非常にまれなことで、通常は、すべてのシステム呼び出しをラップしているlibcライブラリーをアプリケーションにリンクします。

リスト1. ia32アセンブリー (このコード・サンプルはダウンロードできます)
.data                   # section declaration
msg:
        .string "Hello, world!\n"
        len = . - msg   # length of our dear string
.text                   # section declaration
                        # we must export the entry point to the ELF linker or
    .global _start      # loader. They conventionally recognize _start as their
                        # entry point. Use ld -e foo to override the default.
_start:
# write our string to stdout
        movl    $len,%edx   # third argument: message length
        movl    $msg,%ecx   # second argument: pointer to message to write
        movl    $1,%ebx     # first argument: file handle (stdout)
        movl    $4,%eax     # system call number (sys_write)
        int     $0x80       # call kernel
# and exit
        movl    $0,%ebx     # first argument: exit code
        movl    $1,%eax     # system call number (sys_exit)
        int     $0x80       # call kernel

Hello World -- PPC32アセンブリー

リスト2は、同じコードを、そのままPowerPCのアセンブリーに変換したものです。

リスト2. PPC32アセンブリー (このコード・サンプルはダウンロードできます)
.data                       # section declaration - variables only
msg:
        .string "Hello, world!\n"
        len = . - msg       # length of our dear string
.text                       # section declaration - begin code
        .global _start
_start:
# write our string to stdout
        li      0,4         # syscall number (sys_write)
        li      3,1         # first argument: file descriptor (stdout)
                            # second argument: pointer to message to write
        lis     4,msg@ha    # load top 16 bits of &msg
        addi    4,4,msg@l   # load bottom 16 bits
        li      5,len       # third argument: message length
        sc                  # call kernel
# and exit
        li      0,1         # syscall number (sys_exit)
        li      3,1         # first argument: exit code
        sc                  # call kernel

リスト2についての一般的な解説

PowerPCのアセンブリーでは、すべてのレジスター間演算 (register-to-register operations) に転送先レジスターが必要とされます (RISCアーキテクチャーのため)。転送先レジスターは、必ず、引数リストの最初のほうにきます。

PPC Linuxでシステム呼び出しを行うときには、syscall番号をgpr0 に入れ、引数をgpr3 以降に指定します。syscall番号、引数の順序、および引数の数は、他のPowerPCのオペレーティング・システム (NetBSD、Mac OSなど) とは異なる場合があります。プログラマーが通常システム呼び出しをlibcライブラリーを通して行うのは、これも1つの理由です (ライブラリーがOSに固有な問題を処理してくれます)。

レジスター表記
PowerPCのレジスターには、名前でなく番号が付けられています。初心者にとっては、リテラルとレジスターが区別しにくいため、これがときどき混乱を招くことになります。"3" は、値の3を意味することもあれば、レジスターのgpr3、浮動小数点レジスターのfpr3、あるいは特殊レジスターspr3 を意味することもあります。慣れるしかないですね。

即値命令
li は、"load immediate" のことで、「これはコンパイル時にわかっている定数値であり、その値をこのレジスターにストアせよ」という意味になります。addi も即値命令で、たとえば、addi 3,3,1 は、gpr3 の内容を1だけインクリメントして、その結果をgpr3 にストアするという意味になります。これに対してadd 3,3,1 は、gpr3 の内容をgpr1 の内容ぶんだけインクリメントして、その結果をgpr3 にストアします。

"i" で終わる命令は、通常、即値命令です。

ニーモニック
li は、本当は命令ではなく、ニーモニックです。ニーモニック は、少しプリプロセッサー・マクロに似ています。マクロは、アセンブラーが受け付ける命令ですが、アセンブラーは、それを密かに他の命令群に翻訳します。この場合、li 3,1 は、実際には、addi 3,0,1 と定義されます。

鋭い人は、この2つの命令が、必ずしも同じものではないことに気付いたことと思います。addi は、実際には、gpr0 の内容 に1を足して、その結果をgpr3 にストアするというものですよね。それは、その通りなのですが、ただし、PowerPCの仕様では、gpr0 は、文脈にしたがって、値をもつこともあれば、0として読まれることもあることになっています。この場合 (addi の説明にも、このことは明確に示されているのですが)、0は、レジスターgpr0 ではなく、値0を意味します。

ニーモニックは、アセンブラーの開発者以外には、まったく意味をもたないはずなのですが、ディスアセンブリー出力を見るときには、紛らわしい場合があります。ただ、そういう場合にGNUのobjdump -d が非常に便利で、実際にファイルに示されている命令ではなく、元のニーモニックを表示させることができます。たとえば、objdump は、ori 0,0,0 (実際に使用される命令) の代わりに、ニーモニックnop を表示します。

ポインターのロード
今回のHello Worldの例で最も興味深いのは、msg のアドレスをロードしている部分です。上でも触れたように、PowerPCの命令は、固定長の32ビットです (可変長命令を使用するia32とは対照的です)。32ビットの命令は、すなわち32ビットの整数ということです。この整数は、サイズがまちまちのいくつかのフィールドに分けられます。

リスト3. addiのマシン・コード・フォーマット
--------------------------------------------------------------------------
|    opcode    | src register | dest register |     immediate value      |
|    6 bits    |   5 bits     |    5 bits     |         16 bits          |
--------------------------------------------------------------------------

フィールドの数とそれぞれのフィールドのサイズは、命令によって違ってきますが、重要な点は、これらのフィールドが命令内のスペースを占めるということです。addi の場合、3つのフィールドを命令中に配置すると、加算に使用される即値には16ビットしか残りません。

したがって、li も16ビットの即値しかロードできません。1個の命令で、32ビットのポインターをGPRにロードすることはできません。2個の命令を使い、最初は上位16ビットを、次に下位16ビットをロードする必要があります。@ha ("上位")、@l ("下位") というサフィックスがあるのは、まさにこのためです。(@haの "a" の部分は、符号拡張用です。)折よく、lis ("load immediate shifted" という意味) は、GPRの上位16ビットに直接ロードします。そこで、後は下位16ビットに加算すればよいというわけです。

絶対アドレス (あるいは32ビットの即値) をロードするときには、必ずこの技を使うことになります。これが最もよく使われるのは、グローバル値 (globals) を参照するときです。

リスト4. Hello World -- PPC64アセンブリー

リスト4は、上の32ビットPowerPCの例 (リスト2) とほとんど同じです。PowerPCは、64ビットの仕様で設計され、32ビット・バージョンとして実装されていますが、それだけでなく、PowerPCのユーザー・レベルのプログラムは、それらのバージョン間で多かれ少なかれバイナリー互換となっています。Linuxでは、ppc32のバイナリーは、64ビットのハードウェアでも、まったく問題なく実行できます (32ビットのユーザー空間 (userland) と64ビットのカーネルの両方から見える種類の変数については、あちこち少し書き換えも行われますが)。

リスト4. PPC64アセンブリー (このコード・サンプルはダウンロードできます)
.data                       # section declaration - variables only
msg:
        .string "Hello, world!\n"
        len = . - msg       # length of our dear string
.text                       # section declaration - begin code
        .global _start
        .section        ".opd","aw"
        .align 3
_start:
        .quad   ._start,.TOC.@tocbase,0
        .previous
        .global  ._start
._start:
# write our string to stdout
        li      0,4         # syscall number (sys_write)
        li      3,1         # first argument: file descriptor (stdout)
                            # second argument: pointer to message to write
        # load the address of 'msg':
                            # load high word into the low word of r4:
        lis 4,msg@highest   # load msg bits 48-63 into r4 bits 16-31
        ori 4,4,msg@higher  # load msg bits 32-47 into r4 bits  0-15
        rldicr  4,4,32,31   # rotate r4's low word into r4's high word
                            # load low word into the low word of r4:
        oris    4,4,msg@h   # load msg bits 16-31 into r4 bits 16-31
        ori     4,4,msg@l   # load msg bits  0-15 into r4 bits  0-15
        # done loading the address of 'msg'
        li      5,len       # third argument: message length
        sc                  # call kernel
# and exit
        li      0,1         # syscall number (sys_exit)
        li      3,1         # first argument: exit code
        sc                  # call kernel

ppc32のコード (リスト2) とppc64のコード (リスト4) で違う点は2つだけです。1つ目はポインターのロード方法で、もう1つは .opdセクションについてのアセンブラー指令です。ppc32のコードは、ppc32のバイナリーとしてコンパイルすれば、ppc64 Linuxでも完璧に動作します。

ポインターのロード
ppc32では、レジスターに32ビットの即値をロードするには、命令を2個必要としました。ppc64では、5個必要になります。なぜでしょう。

命令は32ビットの固定長のままですので、1回に16ビットぶんの即値しかロードできません。したがって、少なくとも4個の命令が必要です (64ビット / 1命令当たり16ビット = 4命令)。しかし、64ビットGPRの上位ワードに直接ロードするための命令はありません。そこで、下位ワードをロードしておき、それを上位ワードにシフトして、さらに下位ワードをロードする必要があります。

ローテート命令 (リストにあるrlicr など) は、複雑なことで有名であり、冗談で「チューリング完全」と呼ばれたりしています。64ビットの即値をロードするだけなら、心配する必要はありません。これら5個の命令を1個のマクロに変換すればよいのです。そうすれば、二度とそれについて考える必要はありません。

最後にもう1点。ppc32の例で@ha としたところを、今回は@h としています。これは、その後で、下位16ビットを与えるために、addi ではなくori を使用していることによります。RISCマシンでは、同じことをいろいろな方法で実現できることがよくあります (たとえば、nop の実現方法には、数多くの可能性があります)。

関数ディスクリプター -- .opdセクション
ppc64 Linuxでは、Cの関数foo を定義して呼び出す場合、実際に呼び出すのは、関数のコードのアドレスではありません。アセンブリーで、bl foo などとすると、たちまちプログラムをクラッシュさせることになります。foo というラベルは、実際には、fooの関数ディスクリプターのアドレスを表しています。関数ディスクリプターについては、ppc64 ELF ABI (参考文献参照) に詳しい説明がありますが、ごくかいつまんで説明すると、アセンブリーをCのコードから呼び出せるようにするには、コンパイラーの要請にしたがって、関数ディスクリプター (単に3個のポインターからなる構造体) を用意する必要があります。

ここでは、Cのコードはまったく使用していませんが、ELF ABIも、ELFファイルのエントリー・ポイント (デフォルトで_start) は、関数ディスクリプターを指すこと、としています。したがって、上のコードでも1個関数ディスクリプターを用意する必要があり、それが .opdセクションに記述されているものです。

これらのアセンブラー指令は、gcc -S の出力からほとんどそのままコピーされたものです。みなさんのコードでは、これについても、プリプロセッサー・マクロを利用するとよいでしょう。


さらに学習を進めるには

PowerPCについて、もっと詳しく研究してみたいという方は、手許にPowerPCマシンがあるのであれば、小さなプログラムをgcc -S としてコンパイルすることから始めるとよいでしょう。PowerPCマシンのない方は、PPCクロスコンパイルのミニ・ハウツーなど、参考文献に示したサイトや文書をあたってみてください。また、gdbのpsim (PowerPCシミュレーター) ターゲットもお試しください。みなさんが予想しているよりは簡単だと思います。それに、いろいろと楽しめます。

参考文献

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux
ArticleID=229875
ArticleTitle=PowerPCアセンブリー
publish-date=07012002