Skip to content
This repository was archived by the owner on Feb 5, 2019. It is now read-only.

Commit 6d08185

Browse files
authored
Merge pull request #99 from alexcrichton/fix-wasm-casts
Backporting wasm cast fixes
2 parents 487c636 + be7c9e5 commit 6d08185

11 files changed

+439
-27
lines changed

lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,13 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
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
const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
6772
for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) {

lib/Target/WebAssembly/WebAssembly.td

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ include "llvm/Target/Target.td"
2525

2626
def FeatureSIMD128 : SubtargetFeature<"simd128", "HasSIMD128", "true",
2727
"Enable 128-bit SIMD">;
28+
def FeatureNontrappingFPToInt :
29+
SubtargetFeature<"nontrapping-fptoint",
30+
"HasNontrappingFPToInt", "true",
31+
"Enable non-trapping float-to-int conversion operators">;
2832

2933
//===----------------------------------------------------------------------===//
3034
// Architectures.

lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

+155
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"
@@ -173,6 +174,160 @@ MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/,
173174
return Result;
174175
}
175176

177+
// Lower an fp-to-int conversion operator from the LLVM opcode, which has an
178+
// undefined result on invalid/overflow, to the WebAssembly opcode, which
179+
// traps on invalid/overflow.
180+
static MachineBasicBlock *
181+
LowerFPToInt(
182+
MachineInstr &MI,
183+
DebugLoc DL,
184+
MachineBasicBlock *BB,
185+
const TargetInstrInfo &TII,
186+
bool IsUnsigned,
187+
bool Int64,
188+
bool Float64,
189+
unsigned LoweredOpcode
190+
) {
191+
MachineRegisterInfo &MRI = BB->getParent()->getRegInfo();
192+
193+
unsigned OutReg = MI.getOperand(0).getReg();
194+
unsigned InReg = MI.getOperand(1).getReg();
195+
196+
unsigned Abs = Float64 ? WebAssembly::ABS_F64 : WebAssembly::ABS_F32;
197+
unsigned FConst = Float64 ? WebAssembly::CONST_F64 : WebAssembly::CONST_F32;
198+
unsigned LT = Float64 ? WebAssembly::LT_F64 : WebAssembly::LT_F32;
199+
unsigned GE = Float64 ? WebAssembly::GE_F64 : WebAssembly::GE_F32;
200+
unsigned IConst = Int64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32;
201+
unsigned Eqz = WebAssembly::EQZ_I32;
202+
unsigned And = WebAssembly::AND_I32;
203+
int64_t Limit = Int64 ? INT64_MIN : INT32_MIN;
204+
int64_t Substitute = IsUnsigned ? 0 : Limit;
205+
double CmpVal = IsUnsigned ? -(double)Limit * 2.0 : -(double)Limit;
206+
auto &Context = BB->getParent()->getFunction()->getContext();
207+
Type *Ty = Float64 ? Type::getDoubleTy(Context) : Type::getFloatTy(Context);
208+
209+
const BasicBlock *LLVM_BB = BB->getBasicBlock();
210+
MachineFunction *F = BB->getParent();
211+
MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVM_BB);
212+
MachineBasicBlock *FalseMBB = F->CreateMachineBasicBlock(LLVM_BB);
213+
MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVM_BB);
214+
215+
MachineFunction::iterator It = ++BB->getIterator();
216+
F->insert(It, FalseMBB);
217+
F->insert(It, TrueMBB);
218+
F->insert(It, DoneMBB);
219+
220+
// Transfer the remainder of BB and its successor edges to DoneMBB.
221+
DoneMBB->splice(DoneMBB->begin(), BB,
222+
std::next(MachineBasicBlock::iterator(MI)),
223+
BB->end());
224+
DoneMBB->transferSuccessorsAndUpdatePHIs(BB);
225+
226+
BB->addSuccessor(TrueMBB);
227+
BB->addSuccessor(FalseMBB);
228+
TrueMBB->addSuccessor(DoneMBB);
229+
FalseMBB->addSuccessor(DoneMBB);
230+
231+
unsigned Tmp0, Tmp1, CmpReg, EqzReg, FalseReg, TrueReg;
232+
Tmp0 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
233+
Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
234+
CmpReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
235+
EqzReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
236+
FalseReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg));
237+
TrueReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg));
238+
239+
MI.eraseFromParent();
240+
// For signed numbers, we can do a single comparison to determine whether
241+
// fabs(x) is within range.
242+
if (IsUnsigned) {
243+
Tmp0 = InReg;
244+
} else {
245+
BuildMI(BB, DL, TII.get(Abs), Tmp0)
246+
.addReg(InReg);
247+
}
248+
BuildMI(BB, DL, TII.get(FConst), Tmp1)
249+
.addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, CmpVal)));
250+
BuildMI(BB, DL, TII.get(LT), CmpReg)
251+
.addReg(Tmp0)
252+
.addReg(Tmp1);
253+
254+
// For unsigned numbers, we have to do a separate comparison with zero.
255+
if (IsUnsigned) {
256+
Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
257+
unsigned SecondCmpReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
258+
unsigned AndReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
259+
BuildMI(BB, DL, TII.get(FConst), Tmp1)
260+
.addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, 0.0)));
261+
BuildMI(BB, DL, TII.get(GE), SecondCmpReg)
262+
.addReg(Tmp0)
263+
.addReg(Tmp1);
264+
BuildMI(BB, DL, TII.get(And), AndReg)
265+
.addReg(CmpReg)
266+
.addReg(SecondCmpReg);
267+
CmpReg = AndReg;
268+
}
269+
270+
BuildMI(BB, DL, TII.get(Eqz), EqzReg)
271+
.addReg(CmpReg);
272+
273+
// Create the CFG diamond to select between doing the conversion or using
274+
// the substitute value.
275+
BuildMI(BB, DL, TII.get(WebAssembly::BR_IF))
276+
.addMBB(TrueMBB)
277+
.addReg(EqzReg);
278+
BuildMI(FalseMBB, DL, TII.get(LoweredOpcode), FalseReg)
279+
.addReg(InReg);
280+
BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR))
281+
.addMBB(DoneMBB);
282+
BuildMI(TrueMBB, DL, TII.get(IConst), TrueReg)
283+
.addImm(Substitute);
284+
BuildMI(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg)
285+
.addReg(FalseReg)
286+
.addMBB(FalseMBB)
287+
.addReg(TrueReg)
288+
.addMBB(TrueMBB);
289+
290+
return DoneMBB;
291+
}
292+
293+
MachineBasicBlock *
294+
WebAssemblyTargetLowering::EmitInstrWithCustomInserter(
295+
MachineInstr &MI,
296+
MachineBasicBlock *BB
297+
) const {
298+
const TargetInstrInfo &TII = *Subtarget->getInstrInfo();
299+
DebugLoc DL = MI.getDebugLoc();
300+
301+
switch (MI.getOpcode()) {
302+
default: llvm_unreachable("Unexpected instr type to insert");
303+
case WebAssembly::FP_TO_SINT_I32_F32:
304+
return LowerFPToInt(MI, DL, BB, TII, false, false, false,
305+
WebAssembly::I32_TRUNC_S_F32);
306+
case WebAssembly::FP_TO_UINT_I32_F32:
307+
return LowerFPToInt(MI, DL, BB, TII, true, false, false,
308+
WebAssembly::I32_TRUNC_U_F32);
309+
case WebAssembly::FP_TO_SINT_I64_F32:
310+
return LowerFPToInt(MI, DL, BB, TII, false, true, false,
311+
WebAssembly::I64_TRUNC_S_F32);
312+
case WebAssembly::FP_TO_UINT_I64_F32:
313+
return LowerFPToInt(MI, DL, BB, TII, true, true, false,
314+
WebAssembly::I64_TRUNC_U_F32);
315+
case WebAssembly::FP_TO_SINT_I32_F64:
316+
return LowerFPToInt(MI, DL, BB, TII, false, false, true,
317+
WebAssembly::I32_TRUNC_S_F64);
318+
case WebAssembly::FP_TO_UINT_I32_F64:
319+
return LowerFPToInt(MI, DL, BB, TII, true, false, true,
320+
WebAssembly::I32_TRUNC_U_F64);
321+
case WebAssembly::FP_TO_SINT_I64_F64:
322+
return LowerFPToInt(MI, DL, BB, TII, false, true, true,
323+
WebAssembly::I64_TRUNC_S_F64);
324+
case WebAssembly::FP_TO_UINT_I64_F64:
325+
return LowerFPToInt(MI, DL, BB, TII, true, true, true,
326+
WebAssembly::I64_TRUNC_U_F64);
327+
llvm_unreachable("Unexpected instruction to emit with custom inserter");
328+
}
329+
}
330+
176331
const char *WebAssemblyTargetLowering::getTargetNodeName(
177332
unsigned Opcode) const {
178333
switch (static_cast<WebAssemblyISD::NodeType>(Opcode)) {

lib/Target/WebAssembly/WebAssemblyISelLowering.h

+3
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,

lib/Target/WebAssembly/WebAssemblyInstrConv.td

+72-16
Original file line numberDiff line numberDiff line change
@@ -35,32 +35,88 @@ def : Pat<(i64 (anyext I32:$src)), (I64_EXTEND_U_I32 I32:$src)>;
3535

3636
let Defs = [ARGUMENTS] in {
3737

38+
// Conversion from floating point to integer instructions which don't trap on
39+
// overflow or invalid.
40+
def I32_TRUNC_S_SAT_F32 : I<(outs I32:$dst), (ins F32:$src),
41+
[(set I32:$dst, (fp_to_sint F32:$src))],
42+
"i32.trunc_s:sat/f32\t$dst, $src", 0xfc00>,
43+
Requires<[HasNontrappingFPToInt]>;
44+
def I32_TRUNC_U_SAT_F32 : I<(outs I32:$dst), (ins F32:$src),
45+
[(set I32:$dst, (fp_to_uint F32:$src))],
46+
"i32.trunc_u:sat/f32\t$dst, $src", 0xfc01>,
47+
Requires<[HasNontrappingFPToInt]>;
48+
def I64_TRUNC_S_SAT_F32 : I<(outs I64:$dst), (ins F32:$src),
49+
[(set I64:$dst, (fp_to_sint F32:$src))],
50+
"i64.trunc_s:sat/f32\t$dst, $src", 0xfc04>,
51+
Requires<[HasNontrappingFPToInt]>;
52+
def I64_TRUNC_U_SAT_F32 : I<(outs I64:$dst), (ins F32:$src),
53+
[(set I64:$dst, (fp_to_uint F32:$src))],
54+
"i64.trunc_u:sat/f32\t$dst, $src", 0xfc05>,
55+
Requires<[HasNontrappingFPToInt]>;
56+
def I32_TRUNC_S_SAT_F64 : I<(outs I32:$dst), (ins F64:$src),
57+
[(set I32:$dst, (fp_to_sint F64:$src))],
58+
"i32.trunc_s:sat/f64\t$dst, $src", 0xfc02>,
59+
Requires<[HasNontrappingFPToInt]>;
60+
def I32_TRUNC_U_SAT_F64 : I<(outs I32:$dst), (ins F64:$src),
61+
[(set I32:$dst, (fp_to_uint F64:$src))],
62+
"i32.trunc_u:sat/f64\t$dst, $src", 0xfc03>,
63+
Requires<[HasNontrappingFPToInt]>;
64+
def I64_TRUNC_S_SAT_F64 : I<(outs I64:$dst), (ins F64:$src),
65+
[(set I64:$dst, (fp_to_sint F64:$src))],
66+
"i64.trunc_s:sat/f64\t$dst, $src", 0xfc06>,
67+
Requires<[HasNontrappingFPToInt]>;
68+
def I64_TRUNC_U_SAT_F64 : I<(outs I64:$dst), (ins F64:$src),
69+
[(set I64:$dst, (fp_to_uint F64:$src))],
70+
"i64.trunc_u:sat/f64\t$dst, $src", 0xfc07>,
71+
Requires<[HasNontrappingFPToInt]>;
72+
73+
// Conversion from floating point to integer pseudo-instructions which don't
74+
// trap on overflow or invalid.
75+
let usesCustomInserter = 1, isCodeGenOnly = 1 in {
76+
def FP_TO_SINT_I32_F32 : I<(outs I32:$dst), (ins F32:$src),
77+
[(set I32:$dst, (fp_to_sint F32:$src))], "", 0>,
78+
Requires<[NotHasNontrappingFPToInt]>;
79+
def FP_TO_UINT_I32_F32 : I<(outs I32:$dst), (ins F32:$src),
80+
[(set I32:$dst, (fp_to_uint F32:$src))], "", 0>,
81+
Requires<[NotHasNontrappingFPToInt]>;
82+
def FP_TO_SINT_I64_F32 : I<(outs I64:$dst), (ins F32:$src),
83+
[(set I64:$dst, (fp_to_sint F32:$src))], "", 0>,
84+
Requires<[NotHasNontrappingFPToInt]>;
85+
def FP_TO_UINT_I64_F32 : I<(outs I64:$dst), (ins F32:$src),
86+
[(set I64:$dst, (fp_to_uint F32:$src))], "", 0>,
87+
Requires<[NotHasNontrappingFPToInt]>;
88+
def FP_TO_SINT_I32_F64 : I<(outs I32:$dst), (ins F64:$src),
89+
[(set I32:$dst, (fp_to_sint F64:$src))], "", 0>,
90+
Requires<[NotHasNontrappingFPToInt]>;
91+
def FP_TO_UINT_I32_F64 : I<(outs I32:$dst), (ins F64:$src),
92+
[(set I32:$dst, (fp_to_uint F64:$src))], "", 0>,
93+
Requires<[NotHasNontrappingFPToInt]>;
94+
def FP_TO_SINT_I64_F64 : I<(outs I64:$dst), (ins F64:$src),
95+
[(set I64:$dst, (fp_to_sint F64:$src))], "", 0>,
96+
Requires<[NotHasNontrappingFPToInt]>;
97+
def FP_TO_UINT_I64_F64 : I<(outs I64:$dst), (ins F64:$src),
98+
[(set I64:$dst, (fp_to_uint F64:$src))], "", 0>,
99+
Requires<[NotHasNontrappingFPToInt]>;
100+
} // usesCustomInserter, isCodeGenOnly = 1
101+
38102
// Conversion from floating point to integer traps on overflow and invalid.
39103
let hasSideEffects = 1 in {
40104
def I32_TRUNC_S_F32 : I<(outs I32:$dst), (ins F32:$src),
41-
[(set I32:$dst, (fp_to_sint F32:$src))],
42-
"i32.trunc_s/f32\t$dst, $src", 0xa8>;
105+
[], "i32.trunc_s/f32\t$dst, $src", 0xa8>;
43106
def I32_TRUNC_U_F32 : I<(outs I32:$dst), (ins F32:$src),
44-
[(set I32:$dst, (fp_to_uint F32:$src))],
45-
"i32.trunc_u/f32\t$dst, $src", 0xa9>;
107+
[], "i32.trunc_u/f32\t$dst, $src", 0xa9>;
46108
def I64_TRUNC_S_F32 : I<(outs I64:$dst), (ins F32:$src),
47-
[(set I64:$dst, (fp_to_sint F32:$src))],
48-
"i64.trunc_s/f32\t$dst, $src", 0xae>;
109+
[], "i64.trunc_s/f32\t$dst, $src", 0xae>;
49110
def I64_TRUNC_U_F32 : I<(outs I64:$dst), (ins F32:$src),
50-
[(set I64:$dst, (fp_to_uint F32:$src))],
51-
"i64.trunc_u/f32\t$dst, $src", 0xaf>;
111+
[], "i64.trunc_u/f32\t$dst, $src", 0xaf>;
52112
def I32_TRUNC_S_F64 : I<(outs I32:$dst), (ins F64:$src),
53-
[(set I32:$dst, (fp_to_sint F64:$src))],
54-
"i32.trunc_s/f64\t$dst, $src", 0xaa>;
113+
[], "i32.trunc_s/f64\t$dst, $src", 0xaa>;
55114
def I32_TRUNC_U_F64 : I<(outs I32:$dst), (ins F64:$src),
56-
[(set I32:$dst, (fp_to_uint F64:$src))],
57-
"i32.trunc_u/f64\t$dst, $src", 0xab>;
115+
[], "i32.trunc_u/f64\t$dst, $src", 0xab>;
58116
def I64_TRUNC_S_F64 : I<(outs I64:$dst), (ins F64:$src),
59-
[(set I64:$dst, (fp_to_sint F64:$src))],
60-
"i64.trunc_s/f64\t$dst, $src", 0xb0>;
117+
[], "i64.trunc_s/f64\t$dst, $src", 0xb0>;
61118
def I64_TRUNC_U_F64 : I<(outs I64:$dst), (ins F64:$src),
62-
[(set I64:$dst, (fp_to_uint F64:$src))],
63-
"i64.trunc_u/f64\t$dst, $src", 0xb1>;
119+
[], "i64.trunc_u/f64\t$dst, $src", 0xb1>;
64120
} // hasSideEffects = 1
65121

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

lib/Target/WebAssembly/WebAssemblyInstrInfo.td

+8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ def HasAddr32 : Predicate<"!Subtarget->hasAddr64()">;
2020
def HasAddr64 : Predicate<"Subtarget->hasAddr64()">;
2121
def HasSIMD128 : Predicate<"Subtarget->hasSIMD128()">,
2222
AssemblerPredicate<"FeatureSIMD128", "simd128">;
23+
def HasNontrappingFPToInt :
24+
Predicate<"Subtarget->hasNontrappingFPToInt()">,
25+
AssemblerPredicate<"FeatureNontrappingFPToInt",
26+
"nontrapping-fptoint">;
27+
def NotHasNontrappingFPToInt :
28+
Predicate<"!Subtarget->hasNontrappingFPToInt()">,
29+
AssemblerPredicate<"!FeatureNontrappingFPToInt",
30+
"nontrapping-fptoint">;
2331

2432
//===----------------------------------------------------------------------===//
2533
// WebAssembly-specific DAG Node Types.

lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ bool WebAssemblyLowerBrUnless::runOnMachineFunction(MachineFunction &MF) {
9999
case NE_F32: Def->setDesc(TII.get(EQ_F32)); Inverted = true; break;
100100
case EQ_F64: Def->setDesc(TII.get(NE_F64)); Inverted = true; break;
101101
case NE_F64: Def->setDesc(TII.get(EQ_F64)); Inverted = true; break;
102+
case EQZ_I32: {
103+
// Invert an eqz by replacing it with its operand.
104+
Cond = Def->getOperand(1).getReg();
105+
Def->eraseFromParent();
106+
Inverted = true;
107+
break;
108+
}
102109
default: break;
103110
}
104111
}

lib/Target/WebAssembly/WebAssemblySubtarget.cpp

+2-1
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-
CPUString(CPU), TargetTriple(TT), FrameLowering(),
44+
HasNontrappingFPToInt(false), CPUString(CPU),
45+
TargetTriple(TT), FrameLowering(),
4546
InstrInfo(initializeSubtargetDependencies(FS)), TSInfo(),
4647
TLInfo(TM, *this) {}
4748

lib/Target/WebAssembly/WebAssemblySubtarget.h

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace llvm {
3030

3131
class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
3232
bool HasSIMD128;
33+
bool HasNontrappingFPToInt;
3334

3435
/// String name of used CPU.
3536
std::string CPUString;
@@ -74,6 +75,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
7475
// Predicates used by WebAssemblyInstrInfo.td.
7576
bool hasAddr64() const { return TargetTriple.isArch64Bit(); }
7677
bool hasSIMD128() const { return HasSIMD128; }
78+
bool hasNontrappingFPToInt() const { return HasNontrappingFPToInt; }
7779

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

0 commit comments

Comments
 (0)