Skip to content

Commit cdd48b8

Browse files
author
Dan Gohman
committed
[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. llvm-svn: 319128
1 parent 9e3381e commit cdd48b8

10 files changed

+399
-28
lines changed

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,13 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
6161
uint64_t Start = OS.tell();
6262

6363
uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI);
64-
assert(Binary < UINT8_MAX && "Multi-byte opcodes not supported yet");
65-
OS << uint8_t(Binary);
64+
if (Binary <= UINT8_MAX) {
65+
OS << uint8_t(Binary);
66+
} else {
67+
assert(Binary <= UINT16_MAX && "Several-byte opcodes not supported yet");
68+
OS << uint8_t(Binary >> 8)
69+
<< uint8_t(Binary);
70+
}
6671

6772
// For br_table instructions, encode the size of the table. In the MCInst,
6873
// there's an index operand, one operand for each table entry, and the

llvm/lib/Target/WebAssembly/WebAssembly.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ def FeatureSIMD128 : SubtargetFeature<"simd128", "HasSIMD128", "true",
2727
"Enable 128-bit SIMD">;
2828
def FeatureAtomics : SubtargetFeature<"atomics", "HasAtomics", "true",
2929
"Enable Atomics">;
30+
def FeatureNontrappingFPToInt :
31+
SubtargetFeature<"nontrapping-fptoint",
32+
"HasNontrappingFPToInt", "true",
33+
"Enable non-trapping float-to-int conversion operators">;
3034

3135
//===----------------------------------------------------------------------===//
3236
// Architectures.

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "WebAssemblyTargetMachine.h"
2020
#include "llvm/CodeGen/Analysis.h"
2121
#include "llvm/CodeGen/CallingConvLower.h"
22+
#include "llvm/CodeGen/MachineInstrBuilder.h"
2223
#include "llvm/CodeGen/MachineJumpTableInfo.h"
2324
#include "llvm/CodeGen/MachineRegisterInfo.h"
2425
#include "llvm/CodeGen/SelectionDAG.h"
@@ -184,6 +185,134 @@ MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/,
184185
return Result;
185186
}
186187

188+
// Lower an fp-to-int conversion operator from the LLVM opcode, which has an
189+
// undefined result on invalid/overflow, to the WebAssembly opcode, which
190+
// traps on invalid/overflow.
191+
static MachineBasicBlock *
192+
LowerFPToInt(
193+
MachineInstr &MI,
194+
DebugLoc DL,
195+
MachineBasicBlock *BB,
196+
const TargetInstrInfo &TII,
197+
bool IsUnsigned,
198+
bool Int64,
199+
bool Float64,
200+
unsigned LoweredOpcode
201+
) {
202+
MachineRegisterInfo &MRI = BB->getParent()->getRegInfo();
203+
204+
unsigned OutReg = MI.getOperand(0).getReg();
205+
unsigned InReg = MI.getOperand(1).getReg();
206+
207+
unsigned Abs = Float64 ? WebAssembly::ABS_F64 : WebAssembly::ABS_F32;
208+
unsigned FConst = Float64 ? WebAssembly::CONST_F64 : WebAssembly::CONST_F32;
209+
unsigned LT = Float64 ? WebAssembly::LT_F64 : WebAssembly::LT_F32;
210+
unsigned IConst = Int64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32;
211+
int64_t Limit = Int64 ? INT64_MIN : INT32_MIN;
212+
int64_t Substitute = IsUnsigned ? 0 : Limit;
213+
double CmpVal = IsUnsigned ? -(double)Limit * 2.0 : -(double)Limit;
214+
auto &Context = BB->getParent()->getFunction()->getContext();
215+
Type *Ty = Float64 ? Type::getDoubleTy(Context) : Type::getFloatTy(Context);
216+
217+
const BasicBlock *LLVM_BB = BB->getBasicBlock();
218+
MachineFunction *F = BB->getParent();
219+
MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVM_BB);
220+
MachineBasicBlock *FalseMBB = F->CreateMachineBasicBlock(LLVM_BB);
221+
MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVM_BB);
222+
223+
MachineFunction::iterator It = ++BB->getIterator();
224+
F->insert(It, FalseMBB);
225+
F->insert(It, TrueMBB);
226+
F->insert(It, DoneMBB);
227+
228+
// Transfer the remainder of BB and its successor edges to DoneMBB.
229+
DoneMBB->splice(DoneMBB->begin(), BB,
230+
std::next(MachineBasicBlock::iterator(MI)),
231+
BB->end());
232+
DoneMBB->transferSuccessorsAndUpdatePHIs(BB);
233+
234+
BB->addSuccessor(TrueMBB);
235+
BB->addSuccessor(FalseMBB);
236+
TrueMBB->addSuccessor(DoneMBB);
237+
FalseMBB->addSuccessor(DoneMBB);
238+
239+
unsigned Tmp0, Tmp1, Tmp2, Tmp3, Tmp4;
240+
Tmp0 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
241+
Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
242+
Tmp2 = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
243+
Tmp3 = MRI.createVirtualRegister(MRI.getRegClass(OutReg));
244+
Tmp4 = MRI.createVirtualRegister(MRI.getRegClass(OutReg));
245+
246+
MI.eraseFromParent();
247+
if (IsUnsigned) {
248+
Tmp0 = InReg;
249+
} else {
250+
BuildMI(BB, DL, TII.get(Abs), Tmp0)
251+
.addReg(InReg);
252+
}
253+
BuildMI(BB, DL, TII.get(FConst), Tmp1)
254+
.addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, CmpVal)));
255+
BuildMI(BB, DL, TII.get(LT), Tmp2)
256+
.addReg(Tmp0)
257+
.addReg(Tmp1);
258+
BuildMI(BB, DL, TII.get(WebAssembly::BR_IF))
259+
.addMBB(TrueMBB)
260+
.addReg(Tmp2);
261+
262+
BuildMI(FalseMBB, DL, TII.get(IConst), Tmp3)
263+
.addImm(Substitute);
264+
BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR))
265+
.addMBB(DoneMBB);
266+
BuildMI(TrueMBB, DL, TII.get(LoweredOpcode), Tmp4)
267+
.addReg(InReg);
268+
269+
BuildMI(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg)
270+
.addReg(Tmp3)
271+
.addMBB(FalseMBB)
272+
.addReg(Tmp4)
273+
.addMBB(TrueMBB);
274+
275+
return DoneMBB;
276+
}
277+
278+
MachineBasicBlock *
279+
WebAssemblyTargetLowering::EmitInstrWithCustomInserter(
280+
MachineInstr &MI,
281+
MachineBasicBlock *BB
282+
) const {
283+
const TargetInstrInfo &TII = *Subtarget->getInstrInfo();
284+
DebugLoc DL = MI.getDebugLoc();
285+
286+
switch (MI.getOpcode()) {
287+
default: llvm_unreachable("Unexpected instr type to insert");
288+
case WebAssembly::FP_TO_SINT_I32_F32:
289+
return LowerFPToInt(MI, DL, BB, TII, false, false, false,
290+
WebAssembly::I32_TRUNC_S_F32);
291+
case WebAssembly::FP_TO_UINT_I32_F32:
292+
return LowerFPToInt(MI, DL, BB, TII, true, false, false,
293+
WebAssembly::I32_TRUNC_U_F32);
294+
case WebAssembly::FP_TO_SINT_I64_F32:
295+
return LowerFPToInt(MI, DL, BB, TII, false, true, false,
296+
WebAssembly::I64_TRUNC_S_F32);
297+
case WebAssembly::FP_TO_UINT_I64_F32:
298+
return LowerFPToInt(MI, DL, BB, TII, true, true, false,
299+
WebAssembly::I64_TRUNC_U_F32);
300+
case WebAssembly::FP_TO_SINT_I32_F64:
301+
return LowerFPToInt(MI, DL, BB, TII, false, false, true,
302+
WebAssembly::I32_TRUNC_S_F64);
303+
case WebAssembly::FP_TO_UINT_I32_F64:
304+
return LowerFPToInt(MI, DL, BB, TII, true, false, true,
305+
WebAssembly::I32_TRUNC_U_F64);
306+
case WebAssembly::FP_TO_SINT_I64_F64:
307+
return LowerFPToInt(MI, DL, BB, TII, false, true, true,
308+
WebAssembly::I64_TRUNC_S_F64);
309+
case WebAssembly::FP_TO_UINT_I64_F64:
310+
return LowerFPToInt(MI, DL, BB, TII, true, true, true,
311+
WebAssembly::I64_TRUNC_U_F64);
312+
llvm_unreachable("Unexpected instruction to emit with custom inserter");
313+
}
314+
}
315+
187316
const char *WebAssemblyTargetLowering::getTargetNodeName(
188317
unsigned Opcode) const {
189318
switch (static_cast<WebAssemblyISD::NodeType>(Opcode)) {

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ class WebAssemblyTargetLowering final : public TargetLowering {
4848
const TargetLibraryInfo *LibInfo) const override;
4949
bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override;
5050
MVT getScalarShiftAmountTy(const DataLayout &DL, EVT) const override;
51+
MachineBasicBlock *
52+
EmitInstrWithCustomInserter(MachineInstr &MI,
53+
MachineBasicBlock *MBB) const override;
5154
const char *getTargetNodeName(unsigned Opcode) const override;
5255
std::pair<unsigned, const TargetRegisterClass *> getRegForInlineAsmConstraint(
5356
const TargetRegisterInfo *TRI, StringRef Constraint,

llvm/lib/Target/WebAssembly/WebAssemblyInstrConv.td

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,32 +53,88 @@ def : Pat<(i64 (anyext I32:$src)), (I64_EXTEND_U_I32 I32:$src)>;
5353

5454
let Defs = [ARGUMENTS] in {
5555

56+
// Conversion from floating point to integer instructions which don't trap on
57+
// overflow or invalid.
58+
def I32_TRUNC_S_SAT_F32 : I<(outs I32:$dst), (ins F32:$src),
59+
[(set I32:$dst, (fp_to_sint F32:$src))],
60+
"i32.trunc_s:sat/f32\t$dst, $src", 0xfc00>,
61+
Requires<[HasNontrappingFPToInt]>;
62+
def I32_TRUNC_U_SAT_F32 : I<(outs I32:$dst), (ins F32:$src),
63+
[(set I32:$dst, (fp_to_uint F32:$src))],
64+
"i32.trunc_u:sat/f32\t$dst, $src", 0xfc01>,
65+
Requires<[HasNontrappingFPToInt]>;
66+
def I64_TRUNC_S_SAT_F32 : I<(outs I64:$dst), (ins F32:$src),
67+
[(set I64:$dst, (fp_to_sint F32:$src))],
68+
"i64.trunc_s:sat/f32\t$dst, $src", 0xfc04>,
69+
Requires<[HasNontrappingFPToInt]>;
70+
def I64_TRUNC_U_SAT_F32 : I<(outs I64:$dst), (ins F32:$src),
71+
[(set I64:$dst, (fp_to_uint F32:$src))],
72+
"i64.trunc_u:sat/f32\t$dst, $src", 0xfc05>,
73+
Requires<[HasNontrappingFPToInt]>;
74+
def I32_TRUNC_S_SAT_F64 : I<(outs I32:$dst), (ins F64:$src),
75+
[(set I32:$dst, (fp_to_sint F64:$src))],
76+
"i32.trunc_s:sat/f64\t$dst, $src", 0xfc02>,
77+
Requires<[HasNontrappingFPToInt]>;
78+
def I32_TRUNC_U_SAT_F64 : I<(outs I32:$dst), (ins F64:$src),
79+
[(set I32:$dst, (fp_to_uint F64:$src))],
80+
"i32.trunc_u:sat/f64\t$dst, $src", 0xfc03>,
81+
Requires<[HasNontrappingFPToInt]>;
82+
def I64_TRUNC_S_SAT_F64 : I<(outs I64:$dst), (ins F64:$src),
83+
[(set I64:$dst, (fp_to_sint F64:$src))],
84+
"i64.trunc_s:sat/f64\t$dst, $src", 0xfc06>,
85+
Requires<[HasNontrappingFPToInt]>;
86+
def I64_TRUNC_U_SAT_F64 : I<(outs I64:$dst), (ins F64:$src),
87+
[(set I64:$dst, (fp_to_uint F64:$src))],
88+
"i64.trunc_u:sat/f64\t$dst, $src", 0xfc07>,
89+
Requires<[HasNontrappingFPToInt]>;
90+
91+
// Conversion from floating point to integer pseudo-instructions which don't
92+
// trap on overflow or invalid.
93+
let usesCustomInserter = 1, isCodeGenOnly = 1 in {
94+
def FP_TO_SINT_I32_F32 : I<(outs I32:$dst), (ins F32:$src),
95+
[(set I32:$dst, (fp_to_sint F32:$src))], "", 0>,
96+
Requires<[NotHasNontrappingFPToInt]>;
97+
def FP_TO_UINT_I32_F32 : I<(outs I32:$dst), (ins F32:$src),
98+
[(set I32:$dst, (fp_to_uint F32:$src))], "", 0>,
99+
Requires<[NotHasNontrappingFPToInt]>;
100+
def FP_TO_SINT_I64_F32 : I<(outs I64:$dst), (ins F32:$src),
101+
[(set I64:$dst, (fp_to_sint F32:$src))], "", 0>,
102+
Requires<[NotHasNontrappingFPToInt]>;
103+
def FP_TO_UINT_I64_F32 : I<(outs I64:$dst), (ins F32:$src),
104+
[(set I64:$dst, (fp_to_uint F32:$src))], "", 0>,
105+
Requires<[NotHasNontrappingFPToInt]>;
106+
def FP_TO_SINT_I32_F64 : I<(outs I32:$dst), (ins F64:$src),
107+
[(set I32:$dst, (fp_to_sint F64:$src))], "", 0>,
108+
Requires<[NotHasNontrappingFPToInt]>;
109+
def FP_TO_UINT_I32_F64 : I<(outs I32:$dst), (ins F64:$src),
110+
[(set I32:$dst, (fp_to_uint F64:$src))], "", 0>,
111+
Requires<[NotHasNontrappingFPToInt]>;
112+
def FP_TO_SINT_I64_F64 : I<(outs I64:$dst), (ins F64:$src),
113+
[(set I64:$dst, (fp_to_sint F64:$src))], "", 0>,
114+
Requires<[NotHasNontrappingFPToInt]>;
115+
def FP_TO_UINT_I64_F64 : I<(outs I64:$dst), (ins F64:$src),
116+
[(set I64:$dst, (fp_to_uint F64:$src))], "", 0>,
117+
Requires<[NotHasNontrappingFPToInt]>;
118+
} // usesCustomInserter, isCodeGenOnly = 1
119+
56120
// Conversion from floating point to integer traps on overflow and invalid.
57121
let hasSideEffects = 1 in {
58122
def I32_TRUNC_S_F32 : I<(outs I32:$dst), (ins F32:$src),
59-
[(set I32:$dst, (fp_to_sint F32:$src))],
60-
"i32.trunc_s/f32\t$dst, $src", 0xa8>;
123+
[], "i32.trunc_s/f32\t$dst, $src", 0xa8>;
61124
def I32_TRUNC_U_F32 : I<(outs I32:$dst), (ins F32:$src),
62-
[(set I32:$dst, (fp_to_uint F32:$src))],
63-
"i32.trunc_u/f32\t$dst, $src", 0xa9>;
125+
[], "i32.trunc_u/f32\t$dst, $src", 0xa9>;
64126
def I64_TRUNC_S_F32 : I<(outs I64:$dst), (ins F32:$src),
65-
[(set I64:$dst, (fp_to_sint F32:$src))],
66-
"i64.trunc_s/f32\t$dst, $src", 0xae>;
127+
[], "i64.trunc_s/f32\t$dst, $src", 0xae>;
67128
def I64_TRUNC_U_F32 : I<(outs I64:$dst), (ins F32:$src),
68-
[(set I64:$dst, (fp_to_uint F32:$src))],
69-
"i64.trunc_u/f32\t$dst, $src", 0xaf>;
129+
[], "i64.trunc_u/f32\t$dst, $src", 0xaf>;
70130
def I32_TRUNC_S_F64 : I<(outs I32:$dst), (ins F64:$src),
71-
[(set I32:$dst, (fp_to_sint F64:$src))],
72-
"i32.trunc_s/f64\t$dst, $src", 0xaa>;
131+
[], "i32.trunc_s/f64\t$dst, $src", 0xaa>;
73132
def I32_TRUNC_U_F64 : I<(outs I32:$dst), (ins F64:$src),
74-
[(set I32:$dst, (fp_to_uint F64:$src))],
75-
"i32.trunc_u/f64\t$dst, $src", 0xab>;
133+
[], "i32.trunc_u/f64\t$dst, $src", 0xab>;
76134
def I64_TRUNC_S_F64 : I<(outs I64:$dst), (ins F64:$src),
77-
[(set I64:$dst, (fp_to_sint F64:$src))],
78-
"i64.trunc_s/f64\t$dst, $src", 0xb0>;
135+
[], "i64.trunc_s/f64\t$dst, $src", 0xb0>;
79136
def I64_TRUNC_U_F64 : I<(outs I64:$dst), (ins F64:$src),
80-
[(set I64:$dst, (fp_to_uint F64:$src))],
81-
"i64.trunc_u/f64\t$dst, $src", 0xb1>;
137+
[], "i64.trunc_u/f64\t$dst, $src", 0xb1>;
82138
} // hasSideEffects = 1
83139

84140
def F32_CONVERT_S_I32 : I<(outs F32:$dst), (ins I32:$src),

llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ def HasSIMD128 : Predicate<"Subtarget->hasSIMD128()">,
2222
AssemblerPredicate<"FeatureSIMD128", "simd128">;
2323
def HasAtomics : Predicate<"Subtarget->hasAtomics()">,
2424
AssemblerPredicate<"FeatureAtomics", "atomics">;
25+
def HasNontrappingFPToInt :
26+
Predicate<"Subtarget->hasNontrappingFPToInt()">,
27+
AssemblerPredicate<"FeatureNontrappingFPToInt",
28+
"nontrapping-fptoint">;
29+
def NotHasNontrappingFPToInt :
30+
Predicate<"!Subtarget->hasNontrappingFPToInt()">,
31+
AssemblerPredicate<"!FeatureNontrappingFPToInt",
32+
"nontrapping-fptoint">;
2533

2634
//===----------------------------------------------------------------------===//
2735
// WebAssembly-specific DAG Node Types.

llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ WebAssemblySubtarget::WebAssemblySubtarget(const Triple &TT,
4141
const std::string &FS,
4242
const TargetMachine &TM)
4343
: WebAssemblyGenSubtargetInfo(TT, CPU, FS), HasSIMD128(false),
44-
HasAtomics(false), CPUString(CPU), TargetTriple(TT), FrameLowering(),
44+
HasAtomics(false), HasNontrappingFPToInt(false), CPUString(CPU),
45+
TargetTriple(TT), FrameLowering(),
4546
InstrInfo(initializeSubtargetDependencies(FS)), TSInfo(),
4647
TLInfo(TM, *this) {}
4748

llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace llvm {
3131
class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
3232
bool HasSIMD128;
3333
bool HasAtomics;
34+
bool HasNontrappingFPToInt;
3435

3536
/// String name of used CPU.
3637
std::string CPUString;
@@ -76,6 +77,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
7677
bool hasAddr64() const { return TargetTriple.isArch64Bit(); }
7778
bool hasSIMD128() const { return HasSIMD128; }
7879
bool hasAtomics() const { return HasAtomics; }
80+
bool hasNontrappingFPToInt() const { return HasNontrappingFPToInt; }
7981

8082
/// Parses features string setting specified subtarget options. Definition of
8183
/// function is auto generated by tblgen.

0 commit comments

Comments
 (0)