We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
以前読みながら気がついたことをメモしたものです。そのまま放置していましたが、整理して共有させていただきます。
誤りとは言えない修正不要な些細なものもありますが、参考なればと思い、それらも含めて報告させていただきます。
グローバル変数に関しては、別の issue ticket を立てます。
P.34 big-endian
RISC-Vの使用でも現在のところリトルエンディアンに従います。
(本書が出版された2022年7月より約2年半前の) 2019/12/13 の版の RISC-V spec から big-endianもsupport されている。
Preface
...
Defined big-endian variant.
P.48 で 2019/06/08 の版を参照していると明記されているので、誤りではないが、本書を読んで誤解する方を減らしたいという意図。
P.90 LLVM IR
ここで LLVM IR が出てくるが、生成方法が説明されるのは P.100。PCで確認しながら読んでいて、躓いた。
clang -emit-llvm -c add.c
P.96 LLVMのビルド
メモリ16GB の Ubuntu PCでもmemory不足で fail しました。
Issue #15 で回答されている
DCMAKE_BUILD_TYPE="Debug"
-DCMAKE_BUILD_TYPE="Release"
ninja -j1
を本サポートページの 「LLVMビルド時の推奨オプション」 に記載すると、読者の方々の助けになると思います。
参考: 最初から ninja に -j1 を指定すると効率が悪いので、まず -k 0 option をつけて、並列 build できるものは build した後に、-j1 を指定すると大幅に時短できます。
ninja
-j1
-k 0
また、同ページの 「macOSでのビルド方法補足」 の Command Line Tools for XCode の install に加えて以下が必要でした。
Homebrew と RISC-V Toolchain の install
make と ninja の install
make
brew install make brew install ninja
P.110 図5
図の右側: %a %b %c -> %add %a %b
%a %b %c
%add %a %b
P.141: MYRISCVXCallingConv.td
MYRISCVXCallingConv.td
- def CSR_LP32 : CalleeSavedRegs<(add RA, SP, FP, S1, (sequence "S%u", 2, 11))>; + def CSR_LP32 : CalleeSavedRegs<(add RA, SP, FP, (sequence "S%u", 1, 11))>;
P.152 にも同様な記述
P.142
... なお、... llvm/lib/Target/MYRISCVZX/ 以下のパスを示しています。
llvm/lib/Target/MYRISCVZX/
ここで build/ 以下のファイルは LLVM が生成するものであることも説明が欲しかった。 (LLVM経験者には自明のことかと思われるが、知らないと、ファイルを探しても見つからず苦労しました。)
build/
P.152 MYRISCVXRegisterInfo.cpp : getCallPreservedMask() コメント
MYRISCVXRegisterInfo.cpp
getCallPreservedMask()
CSR_LP32のリストを... → CSR_LP32のビットマスク (CSR_LP32_RegMask) を...
CSR_LP32_RegMask
P.152 getReservedRegs()
getReservedRegs()
Reserved register に ra が含まれています。一方、
ra
https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp#L84
では ra が含まれていません。
他の sp などは「システムが使用し」ているのに対して、 ra はcompilerの制御下にあるので、使用可能だからではと考えますが、いかがでしょうか?
sp
P.175 total_64bit()
total_64bit()
この例は 「 c + d の計算結果を (桁上がりを捨てて) int型にした後に、符号拡張」する例。(読者が正しく理解していれば、これで問題ない。しかし関数名に惑わされて、誤って理解することを懸念。)
c + d
int
c + d の結果を long long (64bit) で得る場合は以下となる。
long long
long long total_64bit(int a, int b, int c, int d) { return (long long)c + (long long)d; }
total_64bit(int, int, int, int): # @total_64bit(int, int, int, int) srai a1, a2, 31 srai a4, a3, 31 add a0, a3, a2 // `sltu` (Set Less Than Unsigned) で桁上がりがあった時 `a2` に 1 を set。 sltu a2, a0, a3 add a1, a1, a4 add a1, a1, a2 ret
一方ここではRV32で64bitの値を返す例を示している。その目的では、以下でも十分。
long long return_64bit(int a) { return (long long)a; }
return_64bit(int): # @return_64bit(int) srai a1, a0, 31 ret
P.176
...スタックの動作に似ている...
疑問: ここでの「スタック」はどのような物を指しているのか? (私には (ここで説明している) softwareのスタックしか思い当たらない。)
P.202 図17
図中で本文で説明している「0x800を加算した上で12bit右シフトを行っ」ていることを明確に表現することが望ましい。
0x01235 -> 0x01234 + 1
P.203
... RISC-Vアーキテクチャでも同様に定義されており ...
厳密には 「...RISC-V ABI で...」。
(C拡張を除いて) RISC-V ISA では x0 以外に特別な意味を持たせていない。 (RISC-V ISAの美しい特徴の一つ)
P.276 図7
上の箱から順に
誤: 1. tmp = 0x12345678abcを生成 正: 1. tmp = (0x123456789abc + 1)を生成 誤: 1. tmp = 0x123456789を生成 正: 1. tmp = (0x123456789 + 1)を生成 誤: 1. tmp = 0x0123を生成 2. (tmp << 12) と0x456を結合 正: 1. tmp = 0x0123456を生成 2. (tmp << 12) と0x789を結合 誤: 1. tmp = 0x0123を生成 正: 1. tmp = 0x0123を生成 2. (tmp << 12) と0x456を結合
参考: LLVM 14以降で64bit immediateの生成方法が変わっている。
Compiler Explorer で P.279の long_value() 相当を以下を試した結果 (option: -O)
long_value()
-O
long long long_value() { return 0x0123456789abcdefLL; }
RISC-V rv64gc clang 13.0.1 以前 : 4*8 = 32byte (本書で説明されている例と同じ結果)
long_value(): # @long_value() lui a0, 146 addiw a0, a0, -1493 slli a0, a0, 12 addi a0, a0, 965 slli a0, a0, 13 addi a0, a0, -1347 slli a0, a0, 12 addi a0, a0, -529 ret
RISC-V rv64gc clang 14.0.0 以降, もしくは gcc 8.2.0 (このサイトでの最も古いversion) 以降: 8+4*2 = 16byte
.LCPI0_0: .quad 81985529216486895 # 0x123456789abcdef long_value(): # @long_value() lui a0, %hi(.LCPI0_0) ld a0, %lo(.LCPI0_0)(a0) ret
P.285
unsigned の比較のテストがない
unsigned
P.304 __global_pointer$
__global_pointer$
本書に global pointer の説明がない。
cf. RISC-V ELF Specification, Global-pointer Relaxation 参照
図22と図23、それぞれの位置付けが分かりづらい。
修正案:
図23: PICポリシを使った外部関数のアドレス解決とジャンプ
に合わせて、図22の見出しを
図22: PICポリシを使った外部変数のアドレス解決とアクセス
などとする。
P.317 typo
PLT: Proceduce Lookup Table -> Procedure Linkage Table
cf. RISC-V ELF Specification, Procedure Linkage Table
P.319 Disassembly of section .got:
Disassembly of section .got:
# これから8バイトの値が...
6バイト分しか示されていない。
P.319 Disassembly of section .plt:
Disassembly of section .plt:
このコードが最初理解できなかった。
RISC-V ELF Specification, Procedure Linkage Table に同じのコードがコメント付きで記載されており、本書の解説と合わせてようやく理解できた。
以下はさらに本署の例の値をコメントに加えたもの。
.got.plt: PLT 0x10420 # GOT # input: # t1 = update_global@.plt + 12 = 0x10450+12 # t3 = *(update_global@.got.plt) = 0x10420 1: auipc t2, %pcrel_hi(.got.plt) sub t1, t1, t3 # shifted .got.plt offset + hdr size + 12 # t1 = t1 - t3 = 0x10450+12 - 0x10420 = 0x30 + 12 l[w|d] t3, %pcrel_lo(1b)(t2) # _dl_runtime_resolve (in the first entry of GOT) # t3 = *(0x12000) (_dl_runtime_resolve) addi t1, t1, -(hdr size + 12) # shifted .got.plt offset # t1 = 0x30+12 -(hdr size + 12) = 0x10 addi t0, t2, %pcrel_lo(1b) # &.got.plt # t0 = 0x12000 srli t1, t1, log2(16/PTRSIZE) # .got.plt offset # t1 = 0x10 >> log2(16/8) = 0x8 l[w|d] t0, PTRSIZE(t0) # link map # t0 = *(0x12000 + 8) (link map) jr t3 # jump to _dl_runtime_resolve(*(0x12000)) # t0: link map (*(0x12008)), t1: GOT offset (0x8) __libc_start_main@.plt: 0x10440 update_global@.plt: 0x10450 1: auipc t3, %pcrel_hi(update_global@.got.plt) l[w|d] t3, %pcrel_lo(1b)(t3) # t3 = 0x10420 on the first call jalr t1, t3 nop # <- t1 ... .got.plt : GOT 0x12000: _dl_runtime_resolve 0x12008: link map 0x12010: __libc_start_main@.got.plt 0x12018: update_global@.got.plt
gdb で値を確認しようと試みたのですが、これらの code で step 実行することができませんでした。 やり方をご存知の方は教えてください。
gdb
P.321 medlow コードモデル
生成できる最大アドレス: ... 0x8000_0000
生成できる最小アドレス: ... 0x8000_0000 - 1
アドレス範囲: 下記のようにRV32とRV64と分けてシンプルに書く方が分かりやすい。
P.328
誤
...medany コードモデルの場合、PICポリシの場合には...
正
...medany コードモデルの場合、もしくはPICポリシの場合には...
P.325 表5 リロケーション情報
疑問: なぜ (GNU toolchain の ld を流用する前提で、その ld も使っているであろう) R_RISCV_* という名前をそのまま使わずに R_MYRISCVX_* を定義しているのか?
ld
R_RISCV_*
R_MYRISCVX_*
P.331 MYRISCVXISelLowering.cpp
MYRISCVXISelLowering.cpp
前ページのコード例と重複している。
P.333 MYRISCVXISelLowering.cpp
P.330 のコード例と重複している。 再掲するのであれば、ここでは最後の3行のみを示す方が明確。
P.353 上のコード例
# この地点で ... a か b の
... 1, 2, もしくは 3の
P.352-360
疑問: ここで示されているアセンブラのコードは、llvmの出力ではなく、gcc の出力を例として示しているのか?
P.419, P.467 asm volatile
asm volatile
asm が常に volatile と共に使用されるように説明されている。
asm
volatile
しかし GCC Manual, Extended Asm, "6.47.2.1 Volatile" では、 volatile を使用すべきところ、使用すべきではないところ、について解説している。
P.432 図6
readif
readelf
P.446 MYRISCVXMCCodeEmitter.cpp
MYRISCVXMCCodeEmitter.cpp
同じcode が2回引用されている。
P.456 脚注
TAIL 擬似命令は末尾再帰呼び出しに対応した...
"tail call" 自体の意味は、関数の最後の subroutine callに過ぎず、「末尾再帰呼び出し」とは別のもの。
付録1. 関数呼び出しのバリエーションと高度な機能 より:
末尾関数呼び出しとは、ある関数 f1() が関数 f2() の最後の処理として呼び出されるケースを言います。
この後「末尾再帰呼び出し」の解説が続く。
P.476
while-loop: while-loopの前と中で、同じ処理があり冗長。
以上
The text was updated successfully, but these errors were encountered:
No branches or pull requests
以前読みながら気がついたことをメモしたものです。そのまま放置していましたが、整理して共有させていただきます。
誤りとは言えない修正不要な些細なものもありますが、参考なればと思い、それらも含めて報告させていただきます。
グローバル変数に関しては、別の issue ticket を立てます。
P.34 big-endian
(本書が出版された2022年7月より約2年半前の) 2019/12/13 の版の RISC-V spec から big-endianもsupport されている。
P.48 で 2019/06/08 の版を参照していると明記されているので、誤りではないが、本書を読んで誤解する方を減らしたいという意図。
P.90 LLVM IR
ここで LLVM IR が出てくるが、生成方法が説明されるのは P.100。PCで確認しながら読んでいて、躓いた。
P.96 LLVMのビルド
メモリ16GB の Ubuntu PCでもmemory不足で fail しました。
Issue #15 で回答されている
DCMAKE_BUILD_TYPE="Debug"
としているものを-DCMAKE_BUILD_TYPE="Release"
に変更するninja -j1
を指定するを本サポートページの 「LLVMビルド時の推奨オプション」 に記載すると、読者の方々の助けになると思います。
参考: 最初から
ninja
に-j1
を指定すると効率が悪いので、まず-k 0
option をつけて、並列 build できるものは build した後に、-j1
を指定すると大幅に時短できます。また、同ページの 「macOSでのビルド方法補足」 の Command Line Tools for XCode の install に加えて以下が必要でした。
Homebrew と RISC-V Toolchain の install
make
とninja
の installP.110 図5
図の右側:
%a %b %c
->%add %a %b
P.141:
MYRISCVXCallingConv.td
P.152 にも同様な記述
P.142
ここで
build/
以下のファイルは LLVM が生成するものであることも説明が欲しかった。(LLVM経験者には自明のことかと思われるが、知らないと、ファイルを探しても見つからず苦労しました。)
P.152
MYRISCVXRegisterInfo.cpp
:getCallPreservedMask()
コメントCSR_LP32のリストを... → CSR_LP32のビットマスク (
CSR_LP32_RegMask
) を...P.152
getReservedRegs()
Reserved register に
ra
が含まれています。一方、https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp#L84
では
ra
が含まれていません。他の
sp
などは「システムが使用し」ているのに対して、ra
はcompilerの制御下にあるので、使用可能だからではと考えますが、いかがでしょうか?P.175
total_64bit()
この例は 「
c + d
の計算結果を (桁上がりを捨てて)int
型にした後に、符号拡張」する例。(読者が正しく理解していれば、これで問題ない。しかし関数名に惑わされて、誤って理解することを懸念。)c + d
の結果をlong long
(64bit) で得る場合は以下となる。一方ここではRV32で64bitの値を返す例を示している。その目的では、以下でも十分。
P.176
疑問: ここでの「スタック」はどのような物を指しているのか?
(私には (ここで説明している) softwareのスタックしか思い当たらない。)
P.202 図17
図中で本文で説明している「0x800を加算した上で12bit右シフトを行っ」ていることを明確に表現することが望ましい。
0x01235 -> 0x01234 + 1
P.203
厳密には 「...RISC-V ABI で...」。
(C拡張を除いて) RISC-V ISA では x0 以外に特別な意味を持たせていない。 (RISC-V ISAの美しい特徴の一つ)
P.276 図7
上の箱から順に
参考: LLVM 14以降で64bit immediateの生成方法が変わっている。
Compiler Explorer で P.279の
long_value()
相当を以下を試した結果 (option:-O
)RISC-V rv64gc clang 13.0.1 以前 : 4*8 = 32byte (本書で説明されている例と同じ結果)
RISC-V rv64gc clang 14.0.0 以降, もしくは gcc 8.2.0 (このサイトでの最も古いversion) 以降: 8+4*2 = 16byte
P.285
unsigned
の比較のテストがないP.304
__global_pointer$
本書に global pointer の説明がない。
cf. RISC-V ELF Specification, Global-pointer Relaxation 参照
図22と図23、それぞれの位置付けが分かりづらい。
修正案:
に合わせて、図22の見出しを
などとする。
P.317 typo
PLT: Proceduce Lookup Table -> Procedure Linkage Table
cf. RISC-V ELF Specification, Procedure Linkage Table
P.319
Disassembly of section .got:
6バイト分しか示されていない。
P.319
Disassembly of section .plt:
このコードが最初理解できなかった。
RISC-V ELF Specification, Procedure Linkage Table に同じのコードがコメント付きで記載されており、本書の解説と合わせてようやく理解できた。
以下はさらに本署の例の値をコメントに加えたもの。
gdb
で値を確認しようと試みたのですが、これらの code で step 実行することができませんでした。やり方をご存知の方は教えてください。
P.321 medlow コードモデル
アドレス範囲: 下記のようにRV32とRV64と分けてシンプルに書く方が分かりやすい。
P.328
誤
正
P.325 表5 リロケーション情報
疑問: なぜ (GNU toolchain の
ld
を流用する前提で、そのld
も使っているであろう)R_RISCV_*
という名前をそのまま使わずにR_MYRISCVX_*
を定義しているのか?P.331
MYRISCVXISelLowering.cpp
前ページのコード例と重複している。
P.333
MYRISCVXISelLowering.cpp
P.330 のコード例と重複している。
再掲するのであれば、ここでは最後の3行のみを示す方が明確。
P.353 上のコード例
誤
正
P.352-360
疑問: ここで示されているアセンブラのコードは、llvmの出力ではなく、gcc の出力を例として示しているのか?
P.419, P.467
asm volatile
asm
が常にvolatile
と共に使用されるように説明されている。しかし GCC Manual, Extended Asm, "6.47.2.1 Volatile" では、
volatile
を使用すべきところ、使用すべきではないところ、について解説している。P.432 図6
誤
正
P.446
MYRISCVXMCCodeEmitter.cpp
同じcode が2回引用されている。
P.456 脚注
"tail call" 自体の意味は、関数の最後の subroutine callに過ぎず、「末尾再帰呼び出し」とは別のもの。
付録1. 関数呼び出しのバリエーションと高度な機能 より:
この後「末尾再帰呼び出し」の解説が続く。
P.476
while-loop: while-loopの前と中で、同じ処理があり冗長。
以上
The text was updated successfully, but these errors were encountered: