llvm.org GIT mirror llvm / 9f86840
[WebAssembly] Fix trapping behavior in fptosi/fptoui. This adds code to protect WebAssembly's `trunc_s` family of opcodes from values outside their domain. Even though such conversions have full undefined behavior in C/C++, LLVM IR's `fptosi` and `fptoui` do not, and only return undef. This also implements the proposed non-trapping float-to-int conversion feature and uses that instead when available. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@319128 91177308-0d34-0410-b5e6-96231b3b80d8 Dan Gohman 2 years ago
10 changed file(s) with 399 addition(s) and 28 deletion(s). Raw diff Collapse all Expand all
6060 uint64_t Start = OS.tell();
6161
6262 uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI);
63 assert(Binary < UINT8_MAX && "Multi-byte opcodes not supported yet");
64 OS << uint8_t(Binary);
63 if (Binary <= UINT8_MAX) {
64 OS << uint8_t(Binary);
65 } else {
66 assert(Binary <= UINT16_MAX && "Several-byte opcodes not supported yet");
67 OS << uint8_t(Binary >> 8)
68 << uint8_t(Binary);
69 }
6570
6671 // For br_table instructions, encode the size of the table. In the MCInst,
6772 // there's an index operand, one operand for each table entry, and the
2626 "Enable 128-bit SIMD">;
2727 def FeatureAtomics : SubtargetFeature<"atomics", "HasAtomics", "true",
2828 "Enable Atomics">;
29 def FeatureNontrappingFPToInt :
30 SubtargetFeature<"nontrapping-fptoint",
31 "HasNontrappingFPToInt", "true",
32 "Enable non-trapping float-to-int conversion operators">;
2933
3034 //===----------------------------------------------------------------------===//
3135 // Architectures.
1818 #include "WebAssemblyTargetMachine.h"
1919 #include "llvm/CodeGen/Analysis.h"
2020 #include "llvm/CodeGen/CallingConvLower.h"
21 #include "llvm/CodeGen/MachineInstrBuilder.h"
2122 #include "llvm/CodeGen/MachineJumpTableInfo.h"
2223 #include "llvm/CodeGen/MachineRegisterInfo.h"
2324 #include "llvm/CodeGen/SelectionDAG.h"
181182 assert(Result != MVT::INVALID_SIMPLE_VALUE_TYPE &&
182183 "Unable to represent scalar shift amount type");
183184 return Result;
185 }
186
187 // Lower an fp-to-int conversion operator from the LLVM opcode, which has an
188 // undefined result on invalid/overflow, to the WebAssembly opcode, which
189 // traps on invalid/overflow.
190 static MachineBasicBlock *
191 LowerFPToInt(
192 MachineInstr &MI,
193 DebugLoc DL,
194 MachineBasicBlock *BB,
195 const TargetInstrInfo &TII,
196 bool IsUnsigned,
197 bool Int64,
198 bool Float64,
199 unsigned LoweredOpcode
200 ) {
201 MachineRegisterInfo &MRI = BB->getParent()->getRegInfo();
202
203 unsigned OutReg = MI.getOperand(0).getReg();
204 unsigned InReg = MI.getOperand(1).getReg();
205
206 unsigned Abs = Float64 ? WebAssembly::ABS_F64 : WebAssembly::ABS_F32;
207 unsigned FConst = Float64 ? WebAssembly::CONST_F64 : WebAssembly::CONST_F32;
208 unsigned LT = Float64 ? WebAssembly::LT_F64 : WebAssembly::LT_F32;
209 unsigned IConst = Int64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32;
210 int64_t Limit = Int64 ? INT64_MIN : INT32_MIN;
211 int64_t Substitute = IsUnsigned ? 0 : Limit;
212 double CmpVal = IsUnsigned ? -(double)Limit * 2.0 : -(double)Limit;
213 auto &Context = BB->getParent()->getFunction()->getContext();
214 Type *Ty = Float64 ? Type::getDoubleTy(Context) : Type::getFloatTy(Context);
215
216 const BasicBlock *LLVM_BB = BB->getBasicBlock();
217 MachineFunction *F = BB->getParent();
218 MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVM_BB);
219 MachineBasicBlock *FalseMBB = F->CreateMachineBasicBlock(LLVM_BB);
220 MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVM_BB);
221
222 MachineFunction::iterator It = ++BB->getIterator();
223 F->insert(It, FalseMBB);
224 F->insert(It, TrueMBB);
225 F->insert(It, DoneMBB);
226
227 // Transfer the remainder of BB and its successor edges to DoneMBB.
228 DoneMBB->splice(DoneMBB->begin(), BB,
229 std::next(MachineBasicBlock::iterator(MI)),
230 BB->end());
231 DoneMBB->transferSuccessorsAndUpdatePHIs(BB);
232
233 BB->addSuccessor(TrueMBB);
234 BB->addSuccessor(FalseMBB);
235 TrueMBB->addSuccessor(DoneMBB);
236 FalseMBB->addSuccessor(DoneMBB);
237
238 unsigned Tmp0, Tmp1, Tmp2, Tmp3, Tmp4;
239 Tmp0 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
240 Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
241 Tmp2 = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
242 Tmp3 = MRI.createVirtualRegister(MRI.getRegClass(OutReg));
243 Tmp4 = MRI.createVirtualRegister(MRI.getRegClass(OutReg));
244
245 MI.eraseFromParent();
246 if (IsUnsigned) {
247 Tmp0 = InReg;
248 } else {
249 BuildMI(BB, DL, TII.get(Abs), Tmp0)
250 .addReg(InReg);
251 }
252 BuildMI(BB, DL, TII.get(FConst), Tmp1)
253 .addFPImm(cast(ConstantFP::get(Ty, CmpVal)));
254 BuildMI(BB, DL, TII.get(LT), Tmp2)
255 .addReg(Tmp0)
256 .addReg(Tmp1);
257 BuildMI(BB, DL, TII.get(WebAssembly::BR_IF))
258 .addMBB(TrueMBB)
259 .addReg(Tmp2);
260
261 BuildMI(FalseMBB, DL, TII.get(IConst), Tmp3)
262 .addImm(Substitute);
263 BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR))
264 .addMBB(DoneMBB);
265 BuildMI(TrueMBB, DL, TII.get(LoweredOpcode), Tmp4)
266 .addReg(InReg);
267
268 BuildMI(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg)
269 .addReg(Tmp3)
270 .addMBB(FalseMBB)
271 .addReg(Tmp4)
272 .addMBB(TrueMBB);
273
274 return DoneMBB;
275 }
276
277 MachineBasicBlock *
278 WebAssemblyTargetLowering::EmitInstrWithCustomInserter(
279 MachineInstr &MI,
280 MachineBasicBlock *BB
281 ) const {
282 const TargetInstrInfo &TII = *Subtarget->getInstrInfo();
283 DebugLoc DL = MI.getDebugLoc();
284
285 switch (MI.getOpcode()) {
286 default: llvm_unreachable("Unexpected instr type to insert");
287 case WebAssembly::FP_TO_SINT_I32_F32:
288 return LowerFPToInt(MI, DL, BB, TII, false, false, false,
289 WebAssembly::I32_TRUNC_S_F32);
290 case WebAssembly::FP_TO_UINT_I32_F32:
291 return LowerFPToInt(MI, DL, BB, TII, true, false, false,
292 WebAssembly::I32_TRUNC_U_F32);
293 case WebAssembly::FP_TO_SINT_I64_F32:
294 return LowerFPToInt(MI, DL, BB, TII, false, true, false,
295 WebAssembly::I64_TRUNC_S_F32);
296 case WebAssembly::FP_TO_UINT_I64_F32:
297 return LowerFPToInt(MI, DL, BB, TII, true, true, false,
298 WebAssembly::I64_TRUNC_U_F32);
299 case WebAssembly::FP_TO_SINT_I32_F64:
300 return LowerFPToInt(MI, DL, BB, TII, false, false, true,
301 WebAssembly::I32_TRUNC_S_F64);
302 case WebAssembly::FP_TO_UINT_I32_F64:
303 return LowerFPToInt(MI, DL, BB, TII, true, false, true,
304 WebAssembly::I32_TRUNC_U_F64);
305 case WebAssembly::FP_TO_SINT_I64_F64:
306 return LowerFPToInt(MI, DL, BB, TII, false, true, true,
307 WebAssembly::I64_TRUNC_S_F64);
308 case WebAssembly::FP_TO_UINT_I64_F64:
309 return LowerFPToInt(MI, DL, BB, TII, true, true, true,
310 WebAssembly::I64_TRUNC_U_F64);
311 llvm_unreachable("Unexpected instruction to emit with custom inserter");
312 }
184313 }
185314
186315 const char *WebAssemblyTargetLowering::getTargetNodeName(
4747 const TargetLibraryInfo *LibInfo) const override;
4848 bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override;
4949 MVT getScalarShiftAmountTy(const DataLayout &DL, EVT) const override;
50 MachineBasicBlock *
51 EmitInstrWithCustomInserter(MachineInstr &MI,
52 MachineBasicBlock *MBB) const override;
5053 const char *getTargetNodeName(unsigned Opcode) const override;
5154 std::pair getRegForInlineAsmConstraint(
5255 const TargetRegisterInfo *TRI, StringRef Constraint,
5252
5353 let Defs = [ARGUMENTS] in {
5454
55 // Conversion from floating point to integer instructions which don't trap on
56 // overflow or invalid.
57 def I32_TRUNC_S_SAT_F32 : I<(outs I32:$dst), (ins F32:$src),
58 [(set I32:$dst, (fp_to_sint F32:$src))],
59 "i32.trunc_s:sat/f32\t$dst, $src", 0xfc00>,
60 Requires<[HasNontrappingFPToInt]>;
61 def I32_TRUNC_U_SAT_F32 : I<(outs I32:$dst), (ins F32:$src),
62 [(set I32:$dst, (fp_to_uint F32:$src))],
63 "i32.trunc_u:sat/f32\t$dst, $src", 0xfc01>,
64 Requires<[HasNontrappingFPToInt]>;
65 def I64_TRUNC_S_SAT_F32 : I<(outs I64:$dst), (ins F32:$src),
66 [(set I64:$dst, (fp_to_sint F32:$src))],
67 "i64.trunc_s:sat/f32\t$dst, $src", 0xfc04>,
68 Requires<[HasNontrappingFPToInt]>;
69 def I64_TRUNC_U_SAT_F32 : I<(outs I64:$dst), (ins F32:$src),
70 [(set I64:$dst, (fp_to_uint F32:$src))],
71 "i64.trunc_u:sat/f32\t$dst, $src", 0xfc05>,
72 Requires<[HasNontrappingFPToInt]>;
73 def I32_TRUNC_S_SAT_F64 : I<(outs I32:$dst), (ins F64:$src),
74 [(set I32:$dst, (fp_to_sint F64:$src))],
75 "i32.trunc_s:sat/f64\t$dst, $src", 0xfc02>,
76 Requires<[HasNontrappingFPToInt]>;
77 def I32_TRUNC_U_SAT_F64 : I<(outs I32:$dst), (ins F64:$src),
78 [(set I32:$dst, (fp_to_uint F64:$src))],
79 "i32.trunc_u:sat/f64\t$dst, $src", 0xfc03>,
80 Requires<[HasNontrappingFPToInt]>;
81 def I64_TRUNC_S_SAT_F64 : I<(outs I64:$dst), (ins F64:$src),
82 [(set I64:$dst, (fp_to_sint F64:$src))],
83 "i64.trunc_s:sat/f64\t$dst, $src", 0xfc06>,
84 Requires<[HasNontrappingFPToInt]>;
85 def I64_TRUNC_U_SAT_F64 : I<(outs I64:$dst), (ins F64:$src),
86 [(set I64:$dst, (fp_to_uint F64:$src))],
87 "i64.trunc_u:sat/f64\t$dst, $src", 0xfc07>,
88 Requires<[HasNontrappingFPToInt]>;
89
90 // Conversion from floating point to integer pseudo-instructions which don't
91 // trap on overflow or invalid.
92 let usesCustomInserter = 1, isCodeGenOnly = 1 in {
93 def FP_TO_SINT_I32_F32 : I<(outs I32:$dst), (ins F32:$src),
94 [(set I32:$dst, (fp_to_sint F32:$src))], "", 0>,
95 Requires<[NotHasNontrappingFPToInt]>;
96 def FP_TO_UINT_I32_F32 : I<(outs I32:$dst), (ins F32:$src),
97 [(set I32:$dst, (fp_to_uint F32:$src))], "", 0>,
98 Requires<[NotHasNontrappingFPToInt]>;
99 def FP_TO_SINT_I64_F32 : I<(outs I64:$dst), (ins F32:$src),
100 [(set I64:$dst, (fp_to_sint F32:$src))], "", 0>,
101 Requires<[NotHasNontrappingFPToInt]>;
102 def FP_TO_UINT_I64_F32 : I<(outs I64:$dst), (ins F32:$src),
103 [(set I64:$dst, (fp_to_uint F32:$src))], "", 0>,
104 Requires<[NotHasNontrappingFPToInt]>;
105 def FP_TO_SINT_I32_F64 : I<(outs I32:$dst), (ins F64:$src),
106 [(set I32:$dst, (fp_to_sint F64:$src))], "", 0>,
107 Requires<[NotHasNontrappingFPToInt]>;
108 def FP_TO_UINT_I32_F64 : I<(outs I32:$dst), (ins F64:$src),
109 [(set I32:$dst, (fp_to_uint F64:$src))], "", 0>,
110 Requires<[NotHasNontrappingFPToInt]>;
111 def FP_TO_SINT_I64_F64 : I<(outs I64:$dst), (ins F64:$src),
112 [(set I64:$dst, (fp_to_sint F64:$src))], "", 0>,
113 Requires<[NotHasNontrappingFPToInt]>;
114 def FP_TO_UINT_I64_F64 : I<(outs I64:$dst), (ins F64:$src),
115 [(set I64:$dst, (fp_to_uint F64:$src))], "", 0>,
116 Requires<[NotHasNontrappingFPToInt]>;
117 } // usesCustomInserter, isCodeGenOnly = 1
118
55119 // Conversion from floating point to integer traps on overflow and invalid.
56120 let hasSideEffects = 1 in {
57121 def I32_TRUNC_S_F32 : I<(outs I32:$dst), (ins F32:$src),
58 [(set I32:$dst, (fp_to_sint F32:$src))],
59 "i32.trunc_s/f32\t$dst, $src", 0xa8>;
122 [], "i32.trunc_s/f32\t$dst, $src", 0xa8>;
60123 def I32_TRUNC_U_F32 : I<(outs I32:$dst), (ins F32:$src),
61 [(set I32:$dst, (fp_to_uint F32:$src))],
62 "i32.trunc_u/f32\t$dst, $src", 0xa9>;
124 [], "i32.trunc_u/f32\t$dst, $src", 0xa9>;
63125 def I64_TRUNC_S_F32 : I<(outs I64:$dst), (ins F32:$src),
64 [(set I64:$dst, (fp_to_sint F32:$src))],
65 "i64.trunc_s/f32\t$dst, $src", 0xae>;
126 [], "i64.trunc_s/f32\t$dst, $src", 0xae>;
66127 def I64_TRUNC_U_F32 : I<(outs I64:$dst), (ins F32:$src),
67 [(set I64:$dst, (fp_to_uint F32:$src))],
68 "i64.trunc_u/f32\t$dst, $src", 0xaf>;
128 [], "i64.trunc_u/f32\t$dst, $src", 0xaf>;
69129 def I32_TRUNC_S_F64 : I<(outs I32:$dst), (ins F64:$src),
70 [(set I32:$dst, (fp_to_sint F64:$src))],
71 "i32.trunc_s/f64\t$dst, $src", 0xaa>;
130 [], "i32.trunc_s/f64\t$dst, $src", 0xaa>;
72131 def I32_TRUNC_U_F64 : I<(outs I32:$dst), (ins F64:$src),
73 [(set I32:$dst, (fp_to_uint F64:$src))],
74 "i32.trunc_u/f64\t$dst, $src", 0xab>;
132 [], "i32.trunc_u/f64\t$dst, $src", 0xab>;
75133 def I64_TRUNC_S_F64 : I<(outs I64:$dst), (ins F64:$src),
76 [(set I64:$dst, (fp_to_sint F64:$src))],
77 "i64.trunc_s/f64\t$dst, $src", 0xb0>;
134 [], "i64.trunc_s/f64\t$dst, $src", 0xb0>;
78135 def I64_TRUNC_U_F64 : I<(outs I64:$dst), (ins F64:$src),
79 [(set I64:$dst, (fp_to_uint F64:$src))],
80 "i64.trunc_u/f64\t$dst, $src", 0xb1>;
136 [], "i64.trunc_u/f64\t$dst, $src", 0xb1>;
81137 } // hasSideEffects = 1
82138
83139 def F32_CONVERT_S_I32 : I<(outs F32:$dst), (ins I32:$src),
2121 AssemblerPredicate<"FeatureSIMD128", "simd128">;
2222 def HasAtomics : Predicate<"Subtarget->hasAtomics()">,
2323 AssemblerPredicate<"FeatureAtomics", "atomics">;
24 def HasNontrappingFPToInt :
25 Predicate<"Subtarget->hasNontrappingFPToInt()">,
26 AssemblerPredicate<"FeatureNontrappingFPToInt",
27 "nontrapping-fptoint">;
28 def NotHasNontrappingFPToInt :
29 Predicate<"!Subtarget->hasNontrappingFPToInt()">,
30 AssemblerPredicate<"!FeatureNontrappingFPToInt",
31 "nontrapping-fptoint">;
2432
2533 //===----------------------------------------------------------------------===//
2634 // WebAssembly-specific DAG Node Types.
4040 const std::string &FS,
4141 const TargetMachine &TM)
4242 : WebAssemblyGenSubtargetInfo(TT, CPU, FS), HasSIMD128(false),
43 HasAtomics(false), CPUString(CPU), TargetTriple(TT), FrameLowering(),
43 HasAtomics(false), HasNontrappingFPToInt(false), CPUString(CPU),
44 TargetTriple(TT), FrameLowering(),
4445 InstrInfo(initializeSubtargetDependencies(FS)), TSInfo(),
4546 TLInfo(TM, *this) {}
4647
3030 class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
3131 bool HasSIMD128;
3232 bool HasAtomics;
33 bool HasNontrappingFPToInt;
3334
3435 /// String name of used CPU.
3536 std::string CPUString;
7576 bool hasAddr64() const { return TargetTriple.isArch64Bit(); }
7677 bool hasSIMD128() const { return HasSIMD128; }
7778 bool hasAtomics() const { return HasAtomics; }
79 bool hasNontrappingFPToInt() const { return HasNontrappingFPToInt; }
7880
7981 /// Parses features string setting specified subtarget options. Definition of
8082 /// function is auto generated by tblgen.
0 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals -mattr=-nontrapping-fptoint | FileCheck %s
1
2 ; Test that basic conversion operations assemble as expected using
3 ; the trapping opcodes and explicit code to suppress the trapping.
4
5 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
6 target triple = "wasm32-unknown-unknown-wasm"
7
8 ; CHECK-LABEL: i32_trunc_s_f32:
9 ; CHECK-NEXT: .param f32{{$}}
10 ; CHECK-NEXT: .result i32{{$}}
11 ; CHECK-NEXT: block
12 ; CHECK-NEXT: f32.abs $push[[ABS:[0-9]+]]=, $0{{$}}
13 ; CHECK-NEXT: f32.const $push[[LIMIT:[0-9]+]]=, 0x1p31{{$}}
14 ; CHECK-NEXT: f32.lt $push[[LT:[0-9]+]]=, $pop[[ABS]], $pop[[LIMIT]]{{$}}
15 ; CHECK-NEXT: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
16 ; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
17 ; CHECK-NEXT: i32.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
18 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
19 ; CHECK-NEXT: BB
20 ; CHECK-NEXT: end_block
21 ; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, -2147483648{{$}}
22 ; CHECK-NEXT: return $pop[[ALT]]{{$}}
23 define i32 @i32_trunc_s_f32(float %x) {
24 %a = fptosi float %x to i32
25 ret i32 %a
26 }
27
28 ; CHECK-LABEL: i32_trunc_u_f32:
29 ; CHECK-NEXT: .param f32{{$}}
30 ; CHECK-NEXT: .result i32{{$}}
31 ; CHECK-NEXT: block
32 ; CHECK-NEXT: f32.const $push[[LIMIT:[0-9]+]]=, 0x1p32{{$}}
33 ; CHECK-NEXT: f32.lt $push[[LT:[0-9]+]]=, $0, $pop[[LIMIT]]{{$}}
34 ; CHECK-NEXT: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
35 ; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
36 ; CHECK-NEXT: i32.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
37 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
38 ; CHECK-NEXT: BB
39 ; CHECK-NEXT: end_block
40 ; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, 0{{$}}
41 ; CHECK-NEXT: return $pop[[ALT]]{{$}}
42 define i32 @i32_trunc_u_f32(float %x) {
43 %a = fptoui float %x to i32
44 ret i32 %a
45 }
46
47 ; CHECK-LABEL: i32_trunc_s_f64:
48 ; CHECK-NEXT: .param f64{{$}}
49 ; CHECK-NEXT: .result i32{{$}}
50 ; CHECK-NEXT: block
51 ; CHECK-NEXT: f64.abs $push[[ABS:[0-9]+]]=, $0{{$}}
52 ; CHECK-NEXT: f64.const $push[[LIMIT:[0-9]+]]=, 0x1p31{{$}}
53 ; CHECK-NEXT: f64.lt $push[[LT:[0-9]+]]=, $pop[[ABS]], $pop[[LIMIT]]{{$}}
54 ; CHECK-NEXT: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
55 ; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
56 ; CHECK-NEXT: i32.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
57 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
58 ; CHECK-NEXT: BB
59 ; CHECK-NEXT: end_block
60 ; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, -2147483648{{$}}
61 ; CHECK-NEXT: return $pop[[ALT]]{{$}}
62 define i32 @i32_trunc_s_f64(double %x) {
63 %a = fptosi double %x to i32
64 ret i32 %a
65 }
66
67 ; CHECK-LABEL: i32_trunc_u_f64:
68 ; CHECK-NEXT: .param f64{{$}}
69 ; CHECK-NEXT: .result i32{{$}}
70 ; CHECK-NEXT: block
71 ; CHECK-NEXT: f64.const $push[[LIMIT:[0-9]+]]=, 0x1p32{{$}}
72 ; CHECK-NEXT: f64.lt $push[[LT:[0-9]+]]=, $0, $pop[[LIMIT]]{{$}}
73 ; CHECK-NEXT: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
74 ; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
75 ; CHECK-NEXT: i32.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
76 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
77 ; CHECK-NEXT: BB
78 ; CHECK-NEXT: end_block
79 ; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, 0{{$}}
80 ; CHECK-NEXT: return $pop[[ALT]]{{$}}
81 define i32 @i32_trunc_u_f64(double %x) {
82 %a = fptoui double %x to i32
83 ret i32 %a
84 }
85
86 ; CHECK-LABEL: i64_trunc_s_f32:
87 ; CHECK-NEXT: .param f32{{$}}
88 ; CHECK-NEXT: .result i64{{$}}
89 ; CHECK-NEXT: block
90 ; CHECK-NEXT: f32.abs $push[[ABS:[0-9]+]]=, $0{{$}}
91 ; CHECK-NEXT: f32.const $push[[LIMIT:[0-9]+]]=, 0x1p63{{$}}
92 ; CHECK-NEXT: f32.lt $push[[LT:[0-9]+]]=, $pop[[ABS]], $pop[[LIMIT]]{{$}}
93 ; CHECK-NEXT: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
94 ; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
95 ; CHECK-NEXT: i64.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
96 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
97 ; CHECK-NEXT: BB
98 ; CHECK-NEXT: end_block
99 ; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, -9223372036854775808{{$}}
100 ; CHECK-NEXT: return $pop[[ALT]]{{$}}
101 define i64 @i64_trunc_s_f32(float %x) {
102 %a = fptosi float %x to i64
103 ret i64 %a
104 }
105
106 ; CHECK-LABEL: i64_trunc_u_f32:
107 ; CHECK-NEXT: .param f32{{$}}
108 ; CHECK-NEXT: .result i64{{$}}
109 ; CHECK-NEXT: block
110 ; CHECK-NEXT: f32.const $push[[LIMIT:[0-9]+]]=, 0x1p64{{$}}
111 ; CHECK-NEXT: f32.lt $push[[LT:[0-9]+]]=, $0, $pop[[LIMIT]]{{$}}
112 ; CHECK-NEXT: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
113 ; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
114 ; CHECK-NEXT: i64.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
115 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
116 ; CHECK-NEXT: BB
117 ; CHECK-NEXT: end_block
118 ; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, 0{{$}}
119 ; CHECK-NEXT: return $pop[[ALT]]{{$}}
120 define i64 @i64_trunc_u_f32(float %x) {
121 %a = fptoui float %x to i64
122 ret i64 %a
123 }
124
125 ; CHECK-LABEL: i64_trunc_s_f64:
126 ; CHECK-NEXT: .param f64{{$}}
127 ; CHECK-NEXT: .result i64{{$}}
128 ; CHECK-NEXT: block
129 ; CHECK-NEXT: f64.abs $push[[ABS:[0-9]+]]=, $0{{$}}
130 ; CHECK-NEXT: f64.const $push[[LIMIT:[0-9]+]]=, 0x1p63{{$}}
131 ; CHECK-NEXT: f64.lt $push[[LT:[0-9]+]]=, $pop[[ABS]], $pop[[LIMIT]]{{$}}
132 ; CHECK-NEXT: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
133 ; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
134 ; CHECK-NEXT: i64.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
135 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
136 ; CHECK-NEXT: BB
137 ; CHECK-NEXT: end_block
138 ; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, -9223372036854775808{{$}}
139 ; CHECK-NEXT: return $pop[[ALT]]{{$}}
140 define i64 @i64_trunc_s_f64(double %x) {
141 %a = fptosi double %x to i64
142 ret i64 %a
143 }
144
145 ; CHECK-LABEL: i64_trunc_u_f64:
146 ; CHECK-NEXT: .param f64{{$}}
147 ; CHECK-NEXT: .result i64{{$}}
148 ; CHECK-NEXT: block
149 ; CHECK-NEXT: f64.const $push[[LIMIT:[0-9]+]]=, 0x1p64{{$}}
150 ; CHECK-NEXT: f64.lt $push[[LT:[0-9]+]]=, $0, $pop[[LIMIT]]{{$}}
151 ; CHECK-NEXT: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
152 ; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
153 ; CHECK-NEXT: i64.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
154 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
155 ; CHECK-NEXT: BB
156 ; CHECK-NEXT: end_block
157 ; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, 0{{$}}
158 ; CHECK-NEXT: return $pop[[ALT]]{{$}}
159 define i64 @i64_trunc_u_f64(double %x) {
160 %a = fptoui double %x to i64
161 ret i64 %a
162 }
None ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals | FileCheck %s
0 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals -mattr=+nontrapping-fptoint | FileCheck %s
11
22 ; Test that basic conversion operations assemble as expected.
33
3737 ; CHECK-LABEL: i32_trunc_s_f32:
3838 ; CHECK-NEXT: .param f32{{$}}
3939 ; CHECK-NEXT: .result i32{{$}}
40 ; CHECK-NEXT: i32.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
40 ; CHECK-NEXT: i32.trunc_s:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
4141 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
4242 define i32 @i32_trunc_s_f32(float %x) {
4343 %a = fptosi float %x to i32
4747 ; CHECK-LABEL: i32_trunc_u_f32:
4848 ; CHECK-NEXT: .param f32{{$}}
4949 ; CHECK-NEXT: .result i32{{$}}
50 ; CHECK-NEXT: i32.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
50 ; CHECK-NEXT: i32.trunc_u:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
5151 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
5252 define i32 @i32_trunc_u_f32(float %x) {
5353 %a = fptoui float %x to i32
5757 ; CHECK-LABEL: i32_trunc_s_f64:
5858 ; CHECK-NEXT: .param f64{{$}}
5959 ; CHECK-NEXT: .result i32{{$}}
60 ; CHECK-NEXT: i32.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
60 ; CHECK-NEXT: i32.trunc_s:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
6161 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
6262 define i32 @i32_trunc_s_f64(double %x) {
6363 %a = fptosi double %x to i32
6767 ; CHECK-LABEL: i32_trunc_u_f64:
6868 ; CHECK-NEXT: .param f64{{$}}
6969 ; CHECK-NEXT: .result i32{{$}}
70 ; CHECK-NEXT: i32.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
70 ; CHECK-NEXT: i32.trunc_u:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
7171 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
7272 define i32 @i32_trunc_u_f64(double %x) {
7373 %a = fptoui double %x to i32
7777 ; CHECK-LABEL: i64_trunc_s_f32:
7878 ; CHECK-NEXT: .param f32{{$}}
7979 ; CHECK-NEXT: .result i64{{$}}
80 ; CHECK-NEXT: i64.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
80 ; CHECK-NEXT: i64.trunc_s:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
8181 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
8282 define i64 @i64_trunc_s_f32(float %x) {
8383 %a = fptosi float %x to i64
8787 ; CHECK-LABEL: i64_trunc_u_f32:
8888 ; CHECK-NEXT: .param f32{{$}}
8989 ; CHECK-NEXT: .result i64{{$}}
90 ; CHECK-NEXT: i64.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
90 ; CHECK-NEXT: i64.trunc_u:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
9191 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
9292 define i64 @i64_trunc_u_f32(float %x) {
9393 %a = fptoui float %x to i64
9797 ; CHECK-LABEL: i64_trunc_s_f64:
9898 ; CHECK-NEXT: .param f64{{$}}
9999 ; CHECK-NEXT: .result i64{{$}}
100 ; CHECK-NEXT: i64.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
100 ; CHECK-NEXT: i64.trunc_s:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
101101 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
102102 define i64 @i64_trunc_s_f64(double %x) {
103103 %a = fptosi double %x to i64
107107 ; CHECK-LABEL: i64_trunc_u_f64:
108108 ; CHECK-NEXT: .param f64{{$}}
109109 ; CHECK-NEXT: .result i64{{$}}
110 ; CHECK-NEXT: i64.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
110 ; CHECK-NEXT: i64.trunc_u:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
111111 ; CHECK-NEXT: return $pop[[NUM]]{{$}}
112112 define i64 @i64_trunc_u_f64(double %x) {
113113 %a = fptoui double %x to i64