Skip to content

Instantly share code, notes, and snippets.

@kateinoigakukun
Last active April 6, 2022 08:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kateinoigakukun/bf7931278b8ff458e8c59848e6890902 to your computer and use it in GitHub Desktop.
Save kateinoigakukun/bf7931278b8ff458e8c59848e6890902 to your computer and use it in GitHub Desktop.
sNaNがSwift on wasmでバグる
wasm-DEVELOPMENT-SNAPSHOT-2020-04-01-a
#include <stdio.h>
double gen_double_snan() {
unsigned long int tmp = 9219994337134247936;
return *(double *)&tmp;
}
void dump_bits(void *d, size_t size) {
unsigned char *ptr = (unsigned char *) d;
for (int i = size - 1; i >= 0; i--) {
unsigned int mask = 1 << (8 - 1);
unsigned char byte = ptr[i];
do {
putchar(mask & byte ? '1' : '0');
} while(mask >>= 1);
}
putchar('\n');
}
int main(void) {
double d_snan = gen_double_snan();
dump_bits(&d_snan, sizeof(d_snan));
float casted = (float)d_snan;
dump_bits(&casted, sizeof(casted));
return 0;
}
; ModuleID = 'float.ll'
source_filename = "float.ll"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.15.0"
@__swift_reflection_version = linkonce_odr hidden constant i16 3
@llvm.used = appending global [2 x i8*] [i8* bitcast (float ()* @signalingNaN to i8*), i8* bitcast (i16* @__swift_reflection_version to i8*)], section "llvm.metadata", align 8
define swiftcc float @signalingNaN() #0 {
entry:
ret float 0x7FF4000000000000
}
attributes #0 = { "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" }
!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8}
!swift.module.flags = !{!9}
!llvm.linker.options = !{!10, !11}
!llvm.asan.globals = !{!12}
!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 10, i32 15]}
!1 = !{i32 1, !"Objective-C Version", i32 2}
!2 = !{i32 1, !"Objective-C Image Info Version", i32 0}
!3 = !{i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"}
!4 = !{i32 4, !"Objective-C Garbage Collection", i32 83953408}
!5 = !{i32 1, !"Objective-C Class Properties", i32 64}
!6 = !{i32 1, !"wchar_size", i32 4}
!7 = !{i32 7, !"PIC Level", i32 2}
!8 = !{i32 1, !"Swift Version", i32 7}
!9 = !{!"standard-library", i1 false}
!10 = !{!"-lswiftCore"}
!11 = !{!"-lobjc"}
!12 = !{[2 x i8*]* @llvm.used, null, null, i1 false, i1 true}
sil_stage canonical
import Builtin
import Swift
import SwiftShims
sil @signalingNaN : $@convention(method) (@thin Float.Type) -> Float {
bb0(%0 : $@thin Float.Type):
%1 = integer_literal $Builtin.Int32, 2141192192
%2 = builtin "bitcast_Int32_FPIEEE32"(%1 : $Builtin.Int32) : $Builtin.FPIEEE32
%3 = struct $Float (%2 : $Builtin.FPIEEE32)
return %3 : $Float
}
; ModuleID = 'float.wasm.ll'
source_filename = "float.wasm.ll"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-wasi"
@__swift_reflection_version = linkonce_odr hidden constant i16 3
@_swift1_autolink_entries = private constant [12 x i8] c"-lswiftCore\00", section ".swift1_autolink_entries", align 4
@llvm.used = appending global [3 x i8*] [i8* bitcast (float ()* @signalingNaN to i8*), i8* bitcast (i16* @__swift_reflection_version to i8*), i8* getelementptr inbounds ([12 x i8], [12 x i8]* @_swift1_autolink_entries, i32 0, i32 0)], section "llvm.metadata", align 4
define dso_local swiftcc float @signalingNaN() #0 {
entry:
ret float 0x47BC000000000000
}
attributes #0 = { "frame-pointer"="all" "target-cpu"="generic" }
!swift.module.flags = !{!0}
!llvm.linker.options = !{}
!llvm.module.flags = !{!1, !2, !3, !4}
!llvm.asan.globals = !{!5, !6}
!0 = !{!"standard-library", i1 false}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{i32 7, !"PIC Level", i32 2}
!3 = !{i32 4, !"Objective-C Garbage Collection", i32 84084480}
!4 = !{i32 1, !"Swift Version", i32 7}
!5 = !{[12 x i8]* @_swift1_autolink_entries, null, null, i1 false, i1 true}
!6 = !{[3 x i8*]* @llvm.used, null, null, i1 false, i1 true}
float.ll: float.sil
xcrun swiftc -emit-ir float.sil -o float.ll
float.o: float.ll
llc --filetype=obj float.ll
float.wasm: float.sil
swiftc -c float.sil -target wasi-wasm32 -o float.wasm
float.wasm.ll: float.sil
swiftc -emit-ir float.sil -target wasi-wasm32 -o float.wasm.ll
convert:
xcrun clang convert.c -o convert
.PHONY: clean
clean:
rm -f float.ll float.o float.wasm float.wasm.ll

わからん

sil @signalingNaN : $@convention(method) (@thin Float.Type) -> Float {
bb0(%0 : $@thin Float.Type):
  %1 = integer_literal $Builtin.Int32, 2141192192
  %2 = builtin "bitcast_Int32_FPIEEE32"(%1 : $Builtin.Int32) : $Builtin.FPIEEE32
  %3 = struct $Float (%2 : $Builtin.FPIEEE32)
  return %3 : $Float
}

このsNaN (float) を返すSIL functionが

$ make float.ll
define swiftcc float @signalingNaN() #0 {
entry:
  ret float 0x7FF4000000000000
}

こうなる。 定数部分がsNaN (double) になる。

x64向けにオブジェクトファイルを吐くと

$ make float.o
$ objdump -s float.o
...
Contents of section __literal4:
 0010 0000a07f                             ....
...

定数がsNaN (float) に戻る。

wasm向けにオブジェクトファイルを吐くと

$ make float.wasm
$ wasm-objdump -d float.wasm

float.wasm:  file format wasm 0x1

Code Disassembly:

00006c func[0] <signalingNaN>:
 00006d: 01 7d                      | local[0] type=f32
 00006f: 43 00 00 e0 7f             | f32.const nan:0x600000
 000074: 21 02                      | local.set 2
 000076: 20 02                      | local.get 2
 000078: 0f                         | return
 000079: 0b                         | end

f32.constのオペランド部分が 0x7fe00000、qNaN (float) になる。

疑問点

  1. floatなのに64bit doubleに拡張されるのはなぜ?
  2. wasmの時だけqNaNに化けるのはなぜ?

原因

https://github.com/llvm/llvm-project/blob/71f1ab53544c3c379f4d2e6e9dc95c89d8518fe5/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp#L131-L132

ここでdoubleをfloatにキャストしてるのが問題。

int main(void) {
  double d_snan = gen_double_snan();
  dump_bits(&d_snan, sizeof(d_snan));

  float casted = (float)d_snan;
  dump_bits(&casted, sizeof(casted));
  return 0;
}

これは以下を出力する。キャストするとqNaNビットが立つことがわかる。

0111111111110100000000000000000000000000000000000000000000000000
01111111111000000000000000000000

よく出てくる数字のhex

decimal hex binary 備考
2141192192 0x7fa00000 01111111101000000000000000000000 sNaN (float)
2145386496 0x7fe00000 01111111111000000000000000000000 qNaN (float)
9219994337134247936 0x7ff4000000000000 0111111111110100000000000000000000000000000000000000000000000000 sNaN (double)
9222246136947933184 0x7ffc000000000000 0111111111111100000000000000000000000000000000000000000000000000 qNaN (double)
@kateinoigakukun
Copy link
Author

#include <stdio.h>

float gen_snan(void) {
  unsigned int tmp = 2141192192;
  return *(float *)&tmp;
}

int main(void) {
  float f = gen_snan();
  printf("%f\n", f);
  return 0;
}

CでsNaNを組み立てるコード書いたけど、これも同じような結果になったのでLLVMの問題っぽい

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment