Skip to content

Commit 232fd99

Browse files
committed
[WebAssembly] Trapping fptoint builtins and intrinsics
Summary: The WebAssembly backend lowers fptoint instructions to a code sequence that checks for overflow to avoid traps because fptoint is supposed to be speculatable. These new builtins and intrinsics give users a way to depend on the trapping semantics of the underlying instructions and avoid the extra code generated normally. Patch by coffee and tlively. Reviewers: aheejin Subscribers: dschuff, sbc100, jgravelle-google, hiraditya, sunfish, cfe-commits, llvm-commits Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D68902 llvm-svn: 374856
1 parent 6f0768f commit 232fd99

File tree

6 files changed

+186
-0
lines changed

6 files changed

+186
-0
lines changed

clang/include/clang/Basic/BuiltinsWebAssembly.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ BUILTIN(__builtin_wasm_atomic_wait_i32, "ii*iLLi", "n")
4949
BUILTIN(__builtin_wasm_atomic_wait_i64, "iLLi*LLiLLi", "n")
5050
BUILTIN(__builtin_wasm_atomic_notify, "Uii*Ui", "n")
5151

52+
// Trapping fp-to-int conversions
53+
BUILTIN(__builtin_wasm_trunc_s_i32_f32, "if", "nc")
54+
BUILTIN(__builtin_wasm_trunc_u_i32_f32, "if", "nc")
55+
BUILTIN(__builtin_wasm_trunc_s_i32_f64, "id", "nc")
56+
BUILTIN(__builtin_wasm_trunc_u_i32_f64, "id", "nc")
57+
BUILTIN(__builtin_wasm_trunc_s_i64_f32, "LLif", "nc")
58+
BUILTIN(__builtin_wasm_trunc_u_i64_f32, "LLif", "nc")
59+
BUILTIN(__builtin_wasm_trunc_s_i64_f64, "LLid", "nc")
60+
BUILTIN(__builtin_wasm_trunc_u_i64_f64, "LLid", "nc")
61+
5262
// Saturating fp-to-int conversions
5363
TARGET_BUILTIN(__builtin_wasm_trunc_saturate_s_i32_f32, "if", "nc", "nontrapping-fptoint")
5464
TARGET_BUILTIN(__builtin_wasm_trunc_saturate_u_i32_f32, "if", "nc", "nontrapping-fptoint")

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14020,6 +14020,26 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID,
1402014020
Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_atomic_notify);
1402114021
return Builder.CreateCall(Callee, {Addr, Count});
1402214022
}
14023+
case WebAssembly::BI__builtin_wasm_trunc_s_i32_f32:
14024+
case WebAssembly::BI__builtin_wasm_trunc_s_i32_f64:
14025+
case WebAssembly::BI__builtin_wasm_trunc_s_i64_f32:
14026+
case WebAssembly::BI__builtin_wasm_trunc_s_i64_f64: {
14027+
Value *Src = EmitScalarExpr(E->getArg(0));
14028+
llvm::Type *ResT = ConvertType(E->getType());
14029+
Function *Callee =
14030+
CGM.getIntrinsic(Intrinsic::wasm_trunc_signed, {ResT, Src->getType()});
14031+
return Builder.CreateCall(Callee, {Src});
14032+
}
14033+
case WebAssembly::BI__builtin_wasm_trunc_u_i32_f32:
14034+
case WebAssembly::BI__builtin_wasm_trunc_u_i32_f64:
14035+
case WebAssembly::BI__builtin_wasm_trunc_u_i64_f32:
14036+
case WebAssembly::BI__builtin_wasm_trunc_u_i64_f64: {
14037+
Value *Src = EmitScalarExpr(E->getArg(0));
14038+
llvm::Type *ResT = ConvertType(E->getType());
14039+
Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_trunc_unsigned,
14040+
{ResT, Src->getType()});
14041+
return Builder.CreateCall(Callee, {Src});
14042+
}
1402314043
case WebAssembly::BI__builtin_wasm_trunc_saturate_s_i32_f32:
1402414044
case WebAssembly::BI__builtin_wasm_trunc_saturate_s_i32_f64:
1402514045
case WebAssembly::BI__builtin_wasm_trunc_saturate_s_i64_f32:

clang/test/CodeGen/builtins-wasm.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,54 @@ unsigned int atomic_notify(int *addr, unsigned int count) {
8585
// WEBASSEMBLY64: call i32 @llvm.wasm.atomic.notify(i32* %{{.*}}, i32 %{{.*}})
8686
}
8787

88+
int trunc_s_i32_f32(float f) {
89+
return __builtin_wasm_trunc_s_i32_f32(f);
90+
// WEBASSEMBLY: call i32 @llvm.wasm.trunc.signed.i32.f32(float %f)
91+
// WEBASSEMBLY-NEXT: ret
92+
}
93+
94+
int trunc_u_i32_f32(float f) {
95+
return __builtin_wasm_trunc_u_i32_f32(f);
96+
// WEBASSEMBLY: call i32 @llvm.wasm.trunc.unsigned.i32.f32(float %f)
97+
// WEBASSEMBLY-NEXT: ret
98+
}
99+
100+
int trunc_s_i32_f64(double f) {
101+
return __builtin_wasm_trunc_s_i32_f64(f);
102+
// WEBASSEMBLY: call i32 @llvm.wasm.trunc.signed.i32.f64(double %f)
103+
// WEBASSEMBLY-NEXT: ret
104+
}
105+
106+
int trunc_u_i32_f64(double f) {
107+
return __builtin_wasm_trunc_u_i32_f64(f);
108+
// WEBASSEMBLY: call i32 @llvm.wasm.trunc.unsigned.i32.f64(double %f)
109+
// WEBASSEMBLY-NEXT: ret
110+
}
111+
112+
long long trunc_s_i64_f32(float f) {
113+
return __builtin_wasm_trunc_s_i64_f32(f);
114+
// WEBASSEMBLY: call i64 @llvm.wasm.trunc.signed.i64.f32(float %f)
115+
// WEBASSEMBLY-NEXT: ret
116+
}
117+
118+
long long trunc_u_i64_f32(float f) {
119+
return __builtin_wasm_trunc_u_i64_f32(f);
120+
// WEBASSEMBLY: call i64 @llvm.wasm.trunc.unsigned.i64.f32(float %f)
121+
// WEBASSEMBLY-NEXT: ret
122+
}
123+
124+
long long trunc_s_i64_f64(double f) {
125+
return __builtin_wasm_trunc_s_i64_f64(f);
126+
// WEBASSEMBLY: call i64 @llvm.wasm.trunc.signed.i64.f64(double %f)
127+
// WEBASSEMBLY-NEXT: ret
128+
}
129+
130+
long long trunc_u_i64_f64(double f) {
131+
return __builtin_wasm_trunc_u_i64_f64(f);
132+
// WEBASSEMBLY: call i64 @llvm.wasm.trunc.unsigned.i64.f64(double %f)
133+
// WEBASSEMBLY-NEXT: ret
134+
}
135+
88136
int trunc_saturate_s_i32_f32(float f) {
89137
return __builtin_wasm_trunc_saturate_s_i32_f32(f);
90138
// WEBASSEMBLY: call i32 @llvm.wasm.trunc.saturate.signed.i32.f32(float %f)

llvm/include/llvm/IR/IntrinsicsWebAssembly.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ def int_wasm_memory_grow : Intrinsic<[llvm_anyint_ty],
2323
[llvm_i32_ty, LLVMMatchType<0>],
2424
[]>;
2525

26+
//===----------------------------------------------------------------------===//
27+
// Trapping float-to-int conversions
28+
//===----------------------------------------------------------------------===//
29+
30+
def int_wasm_trunc_signed : Intrinsic<[llvm_anyint_ty],
31+
[llvm_anyfloat_ty],
32+
[IntrNoMem]>;
33+
def int_wasm_trunc_unsigned : Intrinsic<[llvm_anyint_ty],
34+
[llvm_anyfloat_ty],
35+
[IntrNoMem]>;
36+
2637
//===----------------------------------------------------------------------===//
2738
// Saturating float-to-int conversions
2839
//===----------------------------------------------------------------------===//

llvm/lib/Target/WebAssembly/WebAssemblyInstrConv.td

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,23 @@ defm I64_TRUNC_U_F64 : I<(outs I64:$dst), (ins F64:$src), (outs), (ins),
171171
0xb1>;
172172
} // hasSideEffects = 1
173173

174+
def : Pat<(int_wasm_trunc_signed F32:$src),
175+
(I32_TRUNC_S_F32 F32:$src)>;
176+
def : Pat<(int_wasm_trunc_unsigned F32:$src),
177+
(I32_TRUNC_U_F32 F32:$src)>;
178+
def : Pat<(int_wasm_trunc_signed F64:$src),
179+
(I32_TRUNC_S_F64 F64:$src)>;
180+
def : Pat<(int_wasm_trunc_unsigned F64:$src),
181+
(I32_TRUNC_U_F64 F64:$src)>;
182+
def : Pat<(int_wasm_trunc_signed F32:$src),
183+
(I64_TRUNC_S_F32 F32:$src)>;
184+
def : Pat<(int_wasm_trunc_unsigned F32:$src),
185+
(I64_TRUNC_U_F32 F32:$src)>;
186+
def : Pat<(int_wasm_trunc_signed F64:$src),
187+
(I64_TRUNC_S_F64 F64:$src)>;
188+
def : Pat<(int_wasm_trunc_unsigned F64:$src),
189+
(I64_TRUNC_U_F64 F64:$src)>;
190+
174191
defm F32_CONVERT_S_I32 : I<(outs F32:$dst), (ins I32:$src), (outs), (ins),
175192
[(set F32:$dst, (sint_to_fp I32:$src))],
176193
"f32.convert_i32_s\t$dst, $src", "f32.convert_i32_s",

llvm/test/CodeGen/WebAssembly/conv-trap.ll

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,83 @@ define i64 @i64_trunc_u_f64(double %x) {
165165
%a = fptoui double %x to i64
166166
ret i64 %a
167167
}
168+
169+
; CHECK-LABEL: llvm_wasm_trunc_signed_i32_f32:
170+
; CHECK-NEXT: .functype llvm_wasm_trunc_signed_i32_f32 (f32) -> (i32)
171+
; CHECK-NEXT: i32.trunc_f32_s $push[[L0:[0-9]+]]=, $0{{$}}
172+
; CHECK-NEXT: return $pop[[L0]]{{$}}
173+
declare i32 @llvm.wasm.trunc.signed.i32.f32(float)
174+
define i32 @llvm_wasm_trunc_signed_i32_f32(float %f) {
175+
%a = call i32 @llvm.wasm.trunc.signed.i32.f32(float %f)
176+
ret i32 %a
177+
}
178+
179+
; CHECK-LABEL: llvm_wasm_trunc_unsigned_i32_f32:
180+
; CHECK-NEXT: .functype llvm_wasm_trunc_unsigned_i32_f32 (f32) -> (i32)
181+
; CHECK-NEXT: i32.trunc_f32_u $push[[L0:[0-9]+]]=, $0{{$}}
182+
; CHECK-NEXT: return $pop[[L0]]{{$}}
183+
declare i32 @llvm.wasm.trunc.unsigned.i32.f32(float)
184+
define i32 @llvm_wasm_trunc_unsigned_i32_f32(float %f) {
185+
%a = call i32 @llvm.wasm.trunc.unsigned.i32.f32(float %f)
186+
ret i32 %a
187+
}
188+
189+
; CHECK-LABEL: llvm_wasm_trunc_signed_i32_f64:
190+
; CHECK-NEXT: .functype llvm_wasm_trunc_signed_i32_f64 (f64) -> (i32)
191+
; CHECK-NEXT: i32.trunc_f64_s $push[[L0:[0-9]+]]=, $0{{$}}
192+
; CHECK-NEXT: return $pop[[L0]]{{$}}
193+
declare i32 @llvm.wasm.trunc.signed.i32.f64(double)
194+
define i32 @llvm_wasm_trunc_signed_i32_f64(double %f) {
195+
%a = call i32 @llvm.wasm.trunc.signed.i32.f64(double %f)
196+
ret i32 %a
197+
}
198+
199+
; CHECK-LABEL: llvm_wasm_trunc_unsigned_i32_f64:
200+
; CHECK-NEXT: .functype llvm_wasm_trunc_unsigned_i32_f64 (f64) -> (i32)
201+
; CHECK-NEXT: i32.trunc_f64_u $push[[L0:[0-9]+]]=, $0{{$}}
202+
; CHECK-NEXT: return $pop[[L0]]{{$}}
203+
declare i32 @llvm.wasm.trunc.unsigned.i32.f64(double)
204+
define i32 @llvm_wasm_trunc_unsigned_i32_f64(double %f) {
205+
%a = call i32 @llvm.wasm.trunc.unsigned.i32.f64(double %f)
206+
ret i32 %a
207+
}
208+
209+
; CHECK-LABEL: llvm_wasm_trunc_signed_i64_f32:
210+
; CHECK-NEXT: .functype llvm_wasm_trunc_signed_i64_f32 (f32) -> (i64)
211+
; CHECK-NEXT: i64.trunc_f32_s $push[[L0:[0-9]+]]=, $0{{$}}
212+
; CHECK-NEXT: return $pop[[L0]]{{$}}
213+
declare i64 @llvm.wasm.trunc.signed.i64.f32(float)
214+
define i64 @llvm_wasm_trunc_signed_i64_f32(float %f) {
215+
%a = call i64 @llvm.wasm.trunc.signed.i64.f32(float %f)
216+
ret i64 %a
217+
}
218+
219+
; CHECK-LABEL: llvm_wasm_trunc_unsigned_i64_f32:
220+
; CHECK-NEXT: .functype llvm_wasm_trunc_unsigned_i64_f32 (f32) -> (i64)
221+
; CHECK-NEXT: i64.trunc_f32_u $push[[L0:[0-9]+]]=, $0{{$}}
222+
; CHECK-NEXT: return $pop[[L0]]{{$}}
223+
declare i64 @llvm.wasm.trunc.unsigned.i64.f32(float)
224+
define i64 @llvm_wasm_trunc_unsigned_i64_f32(float %f) {
225+
%a = call i64 @llvm.wasm.trunc.unsigned.i64.f32(float %f)
226+
ret i64 %a
227+
}
228+
229+
; CHECK-LABEL: llvm_wasm_trunc_signed_i64_f64:
230+
; CHECK-NEXT: .functype llvm_wasm_trunc_signed_i64_f64 (f64) -> (i64)
231+
; CHECK-NEXT: i64.trunc_f64_s $push[[L0:[0-9]+]]=, $0{{$}}
232+
; CHECK-NEXT: return $pop[[L0]]{{$}}
233+
declare i64 @llvm.wasm.trunc.signed.i64.f64(double)
234+
define i64 @llvm_wasm_trunc_signed_i64_f64(double %f) {
235+
%a = call i64 @llvm.wasm.trunc.signed.i64.f64(double %f)
236+
ret i64 %a
237+
}
238+
239+
; CHECK-LABEL: llvm_wasm_trunc_unsigned_i64_f64:
240+
; CHECK-NEXT: .functype llvm_wasm_trunc_unsigned_i64_f64 (f64) -> (i64)
241+
; CHECK-NEXT: i64.trunc_f64_u $push[[L0:[0-9]+]]=, $0{{$}}
242+
; CHECK-NEXT: return $pop[[L0]]{{$}}
243+
declare i64 @llvm.wasm.trunc.unsigned.i64.f64(double)
244+
define i64 @llvm_wasm_trunc_unsigned_i64_f64(double %f) {
245+
%a = call i64 @llvm.wasm.trunc.unsigned.i64.f64(double %f)
246+
ret i64 %a
247+
}

0 commit comments

Comments
 (0)