diff --git a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp index d0e0eecd3002..fee80b09b0b8 100644 --- a/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -60,8 +60,13 @@ void WebAssemblyMCCodeEmitter::encodeInstruction( uint64_t Start = OS.tell(); uint64_t Binary = getBinaryCodeForInstr(MI, Fixups, STI); - assert(Binary < UINT8_MAX && "Multi-byte opcodes not supported yet"); - OS << uint8_t(Binary); + if (Binary <= UINT8_MAX) { + OS << uint8_t(Binary); + } else { + assert(Binary <= UINT16_MAX && "Several-byte opcodes not supported yet"); + OS << uint8_t(Binary >> 8) + << uint8_t(Binary); + } const MCInstrDesc &Desc = MCII.get(MI.getOpcode()); for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) { diff --git a/lib/Target/WebAssembly/WebAssembly.td b/lib/Target/WebAssembly/WebAssembly.td index f647349d759b..2155743e9442 100644 --- a/lib/Target/WebAssembly/WebAssembly.td +++ b/lib/Target/WebAssembly/WebAssembly.td @@ -25,6 +25,10 @@ include "llvm/Target/Target.td" def FeatureSIMD128 : SubtargetFeature<"simd128", "HasSIMD128", "true", "Enable 128-bit SIMD">; +def FeatureNontrappingFPToInt : + SubtargetFeature<"nontrapping-fptoint", + "HasNontrappingFPToInt", "true", + "Enable non-trapping float-to-int conversion operators">; //===----------------------------------------------------------------------===// // Architectures. diff --git a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index 6a7f75a6b3a1..9bff51cddd78 100644 --- a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -19,6 +19,7 @@ #include "WebAssemblyTargetMachine.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/SelectionDAG.h" @@ -173,6 +174,160 @@ MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/, return Result; } +// Lower an fp-to-int conversion operator from the LLVM opcode, which has an +// undefined result on invalid/overflow, to the WebAssembly opcode, which +// traps on invalid/overflow. +static MachineBasicBlock * +LowerFPToInt( + MachineInstr &MI, + DebugLoc DL, + MachineBasicBlock *BB, + const TargetInstrInfo &TII, + bool IsUnsigned, + bool Int64, + bool Float64, + unsigned LoweredOpcode +) { + MachineRegisterInfo &MRI = BB->getParent()->getRegInfo(); + + unsigned OutReg = MI.getOperand(0).getReg(); + unsigned InReg = MI.getOperand(1).getReg(); + + unsigned Abs = Float64 ? WebAssembly::ABS_F64 : WebAssembly::ABS_F32; + unsigned FConst = Float64 ? WebAssembly::CONST_F64 : WebAssembly::CONST_F32; + unsigned LT = Float64 ? WebAssembly::LT_F64 : WebAssembly::LT_F32; + unsigned GE = Float64 ? WebAssembly::GE_F64 : WebAssembly::GE_F32; + unsigned IConst = Int64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32; + unsigned Eqz = WebAssembly::EQZ_I32; + unsigned And = WebAssembly::AND_I32; + int64_t Limit = Int64 ? INT64_MIN : INT32_MIN; + int64_t Substitute = IsUnsigned ? 0 : Limit; + double CmpVal = IsUnsigned ? -(double)Limit * 2.0 : -(double)Limit; + auto &Context = BB->getParent()->getFunction()->getContext(); + Type *Ty = Float64 ? Type::getDoubleTy(Context) : Type::getFloatTy(Context); + + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction *F = BB->getParent(); + MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *FalseMBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVM_BB); + + MachineFunction::iterator It = ++BB->getIterator(); + F->insert(It, FalseMBB); + F->insert(It, TrueMBB); + F->insert(It, DoneMBB); + + // Transfer the remainder of BB and its successor edges to DoneMBB. + DoneMBB->splice(DoneMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), + BB->end()); + DoneMBB->transferSuccessorsAndUpdatePHIs(BB); + + BB->addSuccessor(TrueMBB); + BB->addSuccessor(FalseMBB); + TrueMBB->addSuccessor(DoneMBB); + FalseMBB->addSuccessor(DoneMBB); + + unsigned Tmp0, Tmp1, CmpReg, EqzReg, FalseReg, TrueReg; + Tmp0 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); + Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); + CmpReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); + EqzReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); + FalseReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); + TrueReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); + + MI.eraseFromParent(); + // For signed numbers, we can do a single comparison to determine whether + // fabs(x) is within range. + if (IsUnsigned) { + Tmp0 = InReg; + } else { + BuildMI(BB, DL, TII.get(Abs), Tmp0) + .addReg(InReg); + } + BuildMI(BB, DL, TII.get(FConst), Tmp1) + .addFPImm(cast(ConstantFP::get(Ty, CmpVal))); + BuildMI(BB, DL, TII.get(LT), CmpReg) + .addReg(Tmp0) + .addReg(Tmp1); + + // For unsigned numbers, we have to do a separate comparison with zero. + if (IsUnsigned) { + Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); + unsigned SecondCmpReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); + unsigned AndReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); + BuildMI(BB, DL, TII.get(FConst), Tmp1) + .addFPImm(cast(ConstantFP::get(Ty, 0.0))); + BuildMI(BB, DL, TII.get(GE), SecondCmpReg) + .addReg(Tmp0) + .addReg(Tmp1); + BuildMI(BB, DL, TII.get(And), AndReg) + .addReg(CmpReg) + .addReg(SecondCmpReg); + CmpReg = AndReg; + } + + BuildMI(BB, DL, TII.get(Eqz), EqzReg) + .addReg(CmpReg); + + // Create the CFG diamond to select between doing the conversion or using + // the substitute value. + BuildMI(BB, DL, TII.get(WebAssembly::BR_IF)) + .addMBB(TrueMBB) + .addReg(EqzReg); + BuildMI(FalseMBB, DL, TII.get(LoweredOpcode), FalseReg) + .addReg(InReg); + BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR)) + .addMBB(DoneMBB); + BuildMI(TrueMBB, DL, TII.get(IConst), TrueReg) + .addImm(Substitute); + BuildMI(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg) + .addReg(FalseReg) + .addMBB(FalseMBB) + .addReg(TrueReg) + .addMBB(TrueMBB); + + return DoneMBB; +} + +MachineBasicBlock * +WebAssemblyTargetLowering::EmitInstrWithCustomInserter( + MachineInstr &MI, + MachineBasicBlock *BB +) const { + const TargetInstrInfo &TII = *Subtarget->getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + switch (MI.getOpcode()) { + default: llvm_unreachable("Unexpected instr type to insert"); + case WebAssembly::FP_TO_SINT_I32_F32: + return LowerFPToInt(MI, DL, BB, TII, false, false, false, + WebAssembly::I32_TRUNC_S_F32); + case WebAssembly::FP_TO_UINT_I32_F32: + return LowerFPToInt(MI, DL, BB, TII, true, false, false, + WebAssembly::I32_TRUNC_U_F32); + case WebAssembly::FP_TO_SINT_I64_F32: + return LowerFPToInt(MI, DL, BB, TII, false, true, false, + WebAssembly::I64_TRUNC_S_F32); + case WebAssembly::FP_TO_UINT_I64_F32: + return LowerFPToInt(MI, DL, BB, TII, true, true, false, + WebAssembly::I64_TRUNC_U_F32); + case WebAssembly::FP_TO_SINT_I32_F64: + return LowerFPToInt(MI, DL, BB, TII, false, false, true, + WebAssembly::I32_TRUNC_S_F64); + case WebAssembly::FP_TO_UINT_I32_F64: + return LowerFPToInt(MI, DL, BB, TII, true, false, true, + WebAssembly::I32_TRUNC_U_F64); + case WebAssembly::FP_TO_SINT_I64_F64: + return LowerFPToInt(MI, DL, BB, TII, false, true, true, + WebAssembly::I64_TRUNC_S_F64); + case WebAssembly::FP_TO_UINT_I64_F64: + return LowerFPToInt(MI, DL, BB, TII, true, true, true, + WebAssembly::I64_TRUNC_U_F64); + llvm_unreachable("Unexpected instruction to emit with custom inserter"); + } +} + const char *WebAssemblyTargetLowering::getTargetNodeName( unsigned Opcode) const { switch (static_cast(Opcode)) { diff --git a/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/lib/Target/WebAssembly/WebAssemblyISelLowering.h index 5bc723028e63..d999a184b7d4 100644 --- a/lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ b/lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -48,6 +48,9 @@ class WebAssemblyTargetLowering final : public TargetLowering { const TargetLibraryInfo *LibInfo) const override; bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override; MVT getScalarShiftAmountTy(const DataLayout &DL, EVT) const override; + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *MBB) const override; const char *getTargetNodeName(unsigned Opcode) const override; std::pair getRegForInlineAsmConstraint( const TargetRegisterInfo *TRI, StringRef Constraint, diff --git a/lib/Target/WebAssembly/WebAssemblyInstrConv.td b/lib/Target/WebAssembly/WebAssemblyInstrConv.td index 29483ba663d5..0240037d713d 100644 --- a/lib/Target/WebAssembly/WebAssemblyInstrConv.td +++ b/lib/Target/WebAssembly/WebAssemblyInstrConv.td @@ -35,32 +35,88 @@ def : Pat<(i64 (anyext I32:$src)), (I64_EXTEND_U_I32 I32:$src)>; let Defs = [ARGUMENTS] in { +// Conversion from floating point to integer instructions which don't trap on +// overflow or invalid. +def I32_TRUNC_S_SAT_F32 : I<(outs I32:$dst), (ins F32:$src), + [(set I32:$dst, (fp_to_sint F32:$src))], + "i32.trunc_s:sat/f32\t$dst, $src", 0xfc00>, + Requires<[HasNontrappingFPToInt]>; +def I32_TRUNC_U_SAT_F32 : I<(outs I32:$dst), (ins F32:$src), + [(set I32:$dst, (fp_to_uint F32:$src))], + "i32.trunc_u:sat/f32\t$dst, $src", 0xfc01>, + Requires<[HasNontrappingFPToInt]>; +def I64_TRUNC_S_SAT_F32 : I<(outs I64:$dst), (ins F32:$src), + [(set I64:$dst, (fp_to_sint F32:$src))], + "i64.trunc_s:sat/f32\t$dst, $src", 0xfc04>, + Requires<[HasNontrappingFPToInt]>; +def I64_TRUNC_U_SAT_F32 : I<(outs I64:$dst), (ins F32:$src), + [(set I64:$dst, (fp_to_uint F32:$src))], + "i64.trunc_u:sat/f32\t$dst, $src", 0xfc05>, + Requires<[HasNontrappingFPToInt]>; +def I32_TRUNC_S_SAT_F64 : I<(outs I32:$dst), (ins F64:$src), + [(set I32:$dst, (fp_to_sint F64:$src))], + "i32.trunc_s:sat/f64\t$dst, $src", 0xfc02>, + Requires<[HasNontrappingFPToInt]>; +def I32_TRUNC_U_SAT_F64 : I<(outs I32:$dst), (ins F64:$src), + [(set I32:$dst, (fp_to_uint F64:$src))], + "i32.trunc_u:sat/f64\t$dst, $src", 0xfc03>, + Requires<[HasNontrappingFPToInt]>; +def I64_TRUNC_S_SAT_F64 : I<(outs I64:$dst), (ins F64:$src), + [(set I64:$dst, (fp_to_sint F64:$src))], + "i64.trunc_s:sat/f64\t$dst, $src", 0xfc06>, + Requires<[HasNontrappingFPToInt]>; +def I64_TRUNC_U_SAT_F64 : I<(outs I64:$dst), (ins F64:$src), + [(set I64:$dst, (fp_to_uint F64:$src))], + "i64.trunc_u:sat/f64\t$dst, $src", 0xfc07>, + Requires<[HasNontrappingFPToInt]>; + +// Conversion from floating point to integer pseudo-instructions which don't +// trap on overflow or invalid. +let usesCustomInserter = 1, isCodeGenOnly = 1 in { +def FP_TO_SINT_I32_F32 : I<(outs I32:$dst), (ins F32:$src), + [(set I32:$dst, (fp_to_sint F32:$src))], "", 0>, + Requires<[NotHasNontrappingFPToInt]>; +def FP_TO_UINT_I32_F32 : I<(outs I32:$dst), (ins F32:$src), + [(set I32:$dst, (fp_to_uint F32:$src))], "", 0>, + Requires<[NotHasNontrappingFPToInt]>; +def FP_TO_SINT_I64_F32 : I<(outs I64:$dst), (ins F32:$src), + [(set I64:$dst, (fp_to_sint F32:$src))], "", 0>, + Requires<[NotHasNontrappingFPToInt]>; +def FP_TO_UINT_I64_F32 : I<(outs I64:$dst), (ins F32:$src), + [(set I64:$dst, (fp_to_uint F32:$src))], "", 0>, + Requires<[NotHasNontrappingFPToInt]>; +def FP_TO_SINT_I32_F64 : I<(outs I32:$dst), (ins F64:$src), + [(set I32:$dst, (fp_to_sint F64:$src))], "", 0>, + Requires<[NotHasNontrappingFPToInt]>; +def FP_TO_UINT_I32_F64 : I<(outs I32:$dst), (ins F64:$src), + [(set I32:$dst, (fp_to_uint F64:$src))], "", 0>, + Requires<[NotHasNontrappingFPToInt]>; +def FP_TO_SINT_I64_F64 : I<(outs I64:$dst), (ins F64:$src), + [(set I64:$dst, (fp_to_sint F64:$src))], "", 0>, + Requires<[NotHasNontrappingFPToInt]>; +def FP_TO_UINT_I64_F64 : I<(outs I64:$dst), (ins F64:$src), + [(set I64:$dst, (fp_to_uint F64:$src))], "", 0>, + Requires<[NotHasNontrappingFPToInt]>; +} // usesCustomInserter, isCodeGenOnly = 1 + // Conversion from floating point to integer traps on overflow and invalid. let hasSideEffects = 1 in { def I32_TRUNC_S_F32 : I<(outs I32:$dst), (ins F32:$src), - [(set I32:$dst, (fp_to_sint F32:$src))], - "i32.trunc_s/f32\t$dst, $src", 0xa8>; + [], "i32.trunc_s/f32\t$dst, $src", 0xa8>; def I32_TRUNC_U_F32 : I<(outs I32:$dst), (ins F32:$src), - [(set I32:$dst, (fp_to_uint F32:$src))], - "i32.trunc_u/f32\t$dst, $src", 0xa9>; + [], "i32.trunc_u/f32\t$dst, $src", 0xa9>; def I64_TRUNC_S_F32 : I<(outs I64:$dst), (ins F32:$src), - [(set I64:$dst, (fp_to_sint F32:$src))], - "i64.trunc_s/f32\t$dst, $src", 0xae>; + [], "i64.trunc_s/f32\t$dst, $src", 0xae>; def I64_TRUNC_U_F32 : I<(outs I64:$dst), (ins F32:$src), - [(set I64:$dst, (fp_to_uint F32:$src))], - "i64.trunc_u/f32\t$dst, $src", 0xaf>; + [], "i64.trunc_u/f32\t$dst, $src", 0xaf>; def I32_TRUNC_S_F64 : I<(outs I32:$dst), (ins F64:$src), - [(set I32:$dst, (fp_to_sint F64:$src))], - "i32.trunc_s/f64\t$dst, $src", 0xaa>; + [], "i32.trunc_s/f64\t$dst, $src", 0xaa>; def I32_TRUNC_U_F64 : I<(outs I32:$dst), (ins F64:$src), - [(set I32:$dst, (fp_to_uint F64:$src))], - "i32.trunc_u/f64\t$dst, $src", 0xab>; + [], "i32.trunc_u/f64\t$dst, $src", 0xab>; def I64_TRUNC_S_F64 : I<(outs I64:$dst), (ins F64:$src), - [(set I64:$dst, (fp_to_sint F64:$src))], - "i64.trunc_s/f64\t$dst, $src", 0xb0>; + [], "i64.trunc_s/f64\t$dst, $src", 0xb0>; def I64_TRUNC_U_F64 : I<(outs I64:$dst), (ins F64:$src), - [(set I64:$dst, (fp_to_uint F64:$src))], - "i64.trunc_u/f64\t$dst, $src", 0xb1>; + [], "i64.trunc_u/f64\t$dst, $src", 0xb1>; } // hasSideEffects = 1 def F32_CONVERT_S_I32 : I<(outs F32:$dst), (ins I32:$src), diff --git a/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/lib/Target/WebAssembly/WebAssemblyInstrInfo.td index dcfd1a42c6aa..4648207ec2c7 100644 --- a/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -20,6 +20,14 @@ def HasAddr32 : Predicate<"!Subtarget->hasAddr64()">; def HasAddr64 : Predicate<"Subtarget->hasAddr64()">; def HasSIMD128 : Predicate<"Subtarget->hasSIMD128()">, AssemblerPredicate<"FeatureSIMD128", "simd128">; +def HasNontrappingFPToInt : + Predicate<"Subtarget->hasNontrappingFPToInt()">, + AssemblerPredicate<"FeatureNontrappingFPToInt", + "nontrapping-fptoint">; +def NotHasNontrappingFPToInt : + Predicate<"!Subtarget->hasNontrappingFPToInt()">, + AssemblerPredicate<"!FeatureNontrappingFPToInt", + "nontrapping-fptoint">; //===----------------------------------------------------------------------===// // WebAssembly-specific DAG Node Types. diff --git a/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp b/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp index 7ea5d05a1b21..c14c2c67920f 100644 --- a/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp +++ b/lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp @@ -99,6 +99,13 @@ bool WebAssemblyLowerBrUnless::runOnMachineFunction(MachineFunction &MF) { case NE_F32: Def->setDesc(TII.get(EQ_F32)); Inverted = true; break; case EQ_F64: Def->setDesc(TII.get(NE_F64)); Inverted = true; break; case NE_F64: Def->setDesc(TII.get(EQ_F64)); Inverted = true; break; + case EQZ_I32: { + // Invert an eqz by replacing it with its operand. + Cond = Def->getOperand(1).getReg(); + Def->eraseFromParent(); + Inverted = true; + break; + } default: break; } } diff --git a/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/lib/Target/WebAssembly/WebAssemblySubtarget.cpp index ce39051b0555..32184bbc1861 100644 --- a/lib/Target/WebAssembly/WebAssemblySubtarget.cpp +++ b/lib/Target/WebAssembly/WebAssemblySubtarget.cpp @@ -41,7 +41,8 @@ WebAssemblySubtarget::WebAssemblySubtarget(const Triple &TT, const std::string &FS, const TargetMachine &TM) : WebAssemblyGenSubtargetInfo(TT, CPU, FS), HasSIMD128(false), - CPUString(CPU), TargetTriple(TT), FrameLowering(), + HasNontrappingFPToInt(false), CPUString(CPU), + TargetTriple(TT), FrameLowering(), InstrInfo(initializeSubtargetDependencies(FS)), TSInfo(), TLInfo(TM, *this) {} diff --git a/lib/Target/WebAssembly/WebAssemblySubtarget.h b/lib/Target/WebAssembly/WebAssemblySubtarget.h index f530a290fa0e..eb9f498ce7ae 100644 --- a/lib/Target/WebAssembly/WebAssemblySubtarget.h +++ b/lib/Target/WebAssembly/WebAssemblySubtarget.h @@ -30,6 +30,7 @@ namespace llvm { class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo { bool HasSIMD128; + bool HasNontrappingFPToInt; /// String name of used CPU. std::string CPUString; @@ -74,6 +75,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo { // Predicates used by WebAssemblyInstrInfo.td. bool hasAddr64() const { return TargetTriple.isArch64Bit(); } bool hasSIMD128() const { return HasSIMD128; } + bool hasNontrappingFPToInt() const { return HasNontrappingFPToInt; } /// Parses features string setting specified subtarget options. Definition of /// function is auto generated by tblgen. diff --git a/test/CodeGen/WebAssembly/conv-trap.ll b/test/CodeGen/WebAssembly/conv-trap.ll new file mode 100644 index 000000000000..e20ed0a45271 --- /dev/null +++ b/test/CodeGen/WebAssembly/conv-trap.ll @@ -0,0 +1,167 @@ +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals -mattr=-nontrapping-fptoint | FileCheck %s + +; Test that basic conversion operations assemble as expected using +; the trapping opcodes and explicit code to suppress the trapping. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown-wasm" + +; CHECK-LABEL: i32_trunc_s_f32: +; CHECK-NEXT: .param f32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: block +; CHECK-NEXT: f32.abs $push[[ABS:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: f32.const $push[[LIMIT:[0-9]+]]=, 0x1p31{{$}} +; CHECK-NEXT: f32.lt $push[[LT:[0-9]+]]=, $pop[[ABS]], $pop[[LIMIT]]{{$}} +; CHECK-NEXT: br_if 0, $pop[[LT]]{{$}} +; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, -2147483648{{$}} +; CHECK-NEXT: return $pop[[ALT]]{{$}} +; CHECK-NEXT: BB +; CHECK-NEXT: end_block +; CHECK-NEXT: i32.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i32 @i32_trunc_s_f32(float %x) { + %a = fptosi float %x to i32 + ret i32 %a +} + +; CHECK-LABEL: i32_trunc_u_f32: +; CHECK-NEXT: .param f32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: block +; CHECK-NEXT: f32.const $push[[LIMIT:[0-9]+]]=, 0x1p32{{$}} +; CHECK-NEXT: f32.lt $push[[LT:[0-9]+]]=, $0, $pop[[LIMIT]]{{$}} +; CHECK-NEXT: f32.const $push[[ZERO:[0-9]+]]=, 0x0p0{{$}} +; CHECK-NEXT: f32.ge $push[[GE:[0-9]+]]=, $0, $pop[[ZERO]]{{$}} +; CHECK-NEXT: i32.and $push[[AND:[0-9]+]]=, $pop[[LT]], $pop[[GE]]{{$}} +; CHECK-NEXT: br_if 0, $pop[[AND]]{{$}} +; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: return $pop[[ALT]]{{$}} +; CHECK-NEXT: BB +; CHECK-NEXT: end_block +; CHECK-NEXT: i32.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i32 @i32_trunc_u_f32(float %x) { + %a = fptoui float %x to i32 + ret i32 %a +} + +; CHECK-LABEL: i32_trunc_s_f64: +; CHECK-NEXT: .param f64{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: block +; CHECK-NEXT: f64.abs $push[[ABS:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: f64.const $push[[LIMIT:[0-9]+]]=, 0x1p31{{$}} +; CHECK-NEXT: f64.lt $push[[LT:[0-9]+]]=, $pop[[ABS]], $pop[[LIMIT]]{{$}} +; CHECK-NEXT: br_if 0, $pop[[LT]]{{$}} +; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, -2147483648{{$}} +; CHECK-NEXT: return $pop[[ALT]]{{$}} +; CHECK-NEXT: BB +; CHECK-NEXT: end_block +; CHECK-NEXT: i32.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i32 @i32_trunc_s_f64(double %x) { + %a = fptosi double %x to i32 + ret i32 %a +} + +; CHECK-LABEL: i32_trunc_u_f64: +; CHECK-NEXT: .param f64{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: block +; CHECK-NEXT: f64.const $push[[LIMIT:[0-9]+]]=, 0x1p32{{$}} +; CHECK-NEXT: f64.lt $push[[LT:[0-9]+]]=, $0, $pop[[LIMIT]]{{$}} +; CHECK-NEXT: f64.const $push[[ZERO:[0-9]+]]=, 0x0p0{{$}} +; CHECK-NEXT: f64.ge $push[[GE:[0-9]+]]=, $0, $pop[[ZERO]]{{$}} +; CHECK-NEXT: i32.and $push[[AND:[0-9]+]]=, $pop[[LT]], $pop[[GE]]{{$}} +; CHECK-NEXT: br_if 0, $pop[[AND]]{{$}} +; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: return $pop[[ALT]]{{$}} +; CHECK-NEXT: BB +; CHECK-NEXT: end_block +; CHECK-NEXT: i32.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i32 @i32_trunc_u_f64(double %x) { + %a = fptoui double %x to i32 + ret i32 %a +} + +; CHECK-LABEL: i64_trunc_s_f32: +; CHECK-NEXT: .param f32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: block +; CHECK-NEXT: f32.abs $push[[ABS:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: f32.const $push[[LIMIT:[0-9]+]]=, 0x1p63{{$}} +; CHECK-NEXT: f32.lt $push[[LT:[0-9]+]]=, $pop[[ABS]], $pop[[LIMIT]]{{$}} +; CHECK-NEXT: br_if 0, $pop[[LT]]{{$}} +; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, -9223372036854775808{{$}} +; CHECK-NEXT: return $pop[[ALT]]{{$}} +; CHECK-NEXT: BB +; CHECK-NEXT: end_block +; CHECK-NEXT: i64.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @i64_trunc_s_f32(float %x) { + %a = fptosi float %x to i64 + ret i64 %a +} + +; CHECK-LABEL: i64_trunc_u_f32: +; CHECK-NEXT: .param f32{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: block +; CHECK-NEXT: f32.const $push[[LIMIT:[0-9]+]]=, 0x1p64{{$}} +; CHECK-NEXT: f32.lt $push[[LT:[0-9]+]]=, $0, $pop[[LIMIT]]{{$}} +; CHECK-NEXT: f32.const $push[[ZERO:[0-9]+]]=, 0x0p0{{$}} +; CHECK-NEXT: f32.ge $push[[GE:[0-9]+]]=, $0, $pop[[ZERO]]{{$}} +; CHECK-NEXT: i32.and $push[[AND:[0-9]+]]=, $pop[[LT]], $pop[[GE]]{{$}} +; CHECK-NEXT: br_if 0, $pop[[AND]]{{$}} +; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: return $pop[[ALT]]{{$}} +; CHECK-NEXT: BB +; CHECK-NEXT: end_block +; CHECK-NEXT: i64.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @i64_trunc_u_f32(float %x) { + %a = fptoui float %x to i64 + ret i64 %a +} + +; CHECK-LABEL: i64_trunc_s_f64: +; CHECK-NEXT: .param f64{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: block +; CHECK-NEXT: f64.abs $push[[ABS:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: f64.const $push[[LIMIT:[0-9]+]]=, 0x1p63{{$}} +; CHECK-NEXT: f64.lt $push[[LT:[0-9]+]]=, $pop[[ABS]], $pop[[LIMIT]]{{$}} +; CHECK-NEXT: br_if 0, $pop[[LT]]{{$}} +; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, -9223372036854775808{{$}} +; CHECK-NEXT: return $pop[[ALT]]{{$}} +; CHECK-NEXT: BB +; CHECK-NEXT: end_block +; CHECK-NEXT: i64.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @i64_trunc_s_f64(double %x) { + %a = fptosi double %x to i64 + ret i64 %a +} + +; CHECK-LABEL: i64_trunc_u_f64: +; CHECK-NEXT: .param f64{{$}} +; CHECK-NEXT: .result i64{{$}} +; CHECK-NEXT: block +; CHECK-NEXT: f64.const $push[[LIMIT:[0-9]+]]=, 0x1p64{{$}} +; CHECK-NEXT: f64.lt $push[[LT:[0-9]+]]=, $0, $pop[[LIMIT]]{{$}} +; CHECK-NEXT: f64.const $push[[ZERO:[0-9]+]]=, 0x0p0{{$}} +; CHECK-NEXT: f64.ge $push[[GE:[0-9]+]]=, $0, $pop[[ZERO]]{{$}} +; CHECK-NEXT: i32.and $push[[AND:[0-9]+]]=, $pop[[LT]], $pop[[GE]]{{$}} +; CHECK-NEXT: br_if 0, $pop[[AND]]{{$}} +; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: return $pop[[ALT]]{{$}} +; CHECK-NEXT: BB +; CHECK-NEXT: end_block +; CHECK-NEXT: i64.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[NUM]]{{$}} +define i64 @i64_trunc_u_f64(double %x) { + %a = fptoui double %x to i64 + ret i64 %a +} diff --git a/test/CodeGen/WebAssembly/conv.ll b/test/CodeGen/WebAssembly/conv.ll index 27cebb117dd4..12a20efa0404 100644 --- a/test/CodeGen/WebAssembly/conv.ll +++ b/test/CodeGen/WebAssembly/conv.ll @@ -1,4 +1,8 @@ +<<<<<<< HEAD ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s +======= +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals -mattr=+nontrapping-fptoint | FileCheck %s +>>>>>>> 9f86840... [WebAssembly] Fix trapping behavior in fptosi/fptoui. ; Test that basic conversion operations assemble as expected. @@ -38,7 +42,7 @@ define i64 @i64_extend_u_i32(i32 %x) { ; CHECK-LABEL: i32_trunc_s_f32: ; CHECK-NEXT: .param f32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: i32.trunc_s:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @i32_trunc_s_f32(float %x) { %a = fptosi float %x to i32 @@ -48,7 +52,7 @@ define i32 @i32_trunc_s_f32(float %x) { ; CHECK-LABEL: i32_trunc_u_f32: ; CHECK-NEXT: .param f32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: i32.trunc_u:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @i32_trunc_u_f32(float %x) { %a = fptoui float %x to i32 @@ -58,7 +62,7 @@ define i32 @i32_trunc_u_f32(float %x) { ; CHECK-LABEL: i32_trunc_s_f64: ; CHECK-NEXT: .param f64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: i32.trunc_s:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @i32_trunc_s_f64(double %x) { %a = fptosi double %x to i32 @@ -68,7 +72,7 @@ define i32 @i32_trunc_s_f64(double %x) { ; CHECK-LABEL: i32_trunc_u_f64: ; CHECK-NEXT: .param f64{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: i32.trunc_u:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @i32_trunc_u_f64(double %x) { %a = fptoui double %x to i32 @@ -78,7 +82,7 @@ define i32 @i32_trunc_u_f64(double %x) { ; CHECK-LABEL: i64_trunc_s_f32: ; CHECK-NEXT: .param f32{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: i64.trunc_s:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i64 @i64_trunc_s_f32(float %x) { %a = fptosi float %x to i64 @@ -88,7 +92,7 @@ define i64 @i64_trunc_s_f32(float %x) { ; CHECK-LABEL: i64_trunc_u_f32: ; CHECK-NEXT: .param f32{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: i64.trunc_u:sat/f32 $push[[NUM:[0-9]+]]=, $0{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i64 @i64_trunc_u_f32(float %x) { %a = fptoui float %x to i64 @@ -98,7 +102,7 @@ define i64 @i64_trunc_u_f32(float %x) { ; CHECK-LABEL: i64_trunc_s_f64: ; CHECK-NEXT: .param f64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: i64.trunc_s:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i64 @i64_trunc_s_f64(double %x) { %a = fptosi double %x to i64 @@ -108,7 +112,7 @@ define i64 @i64_trunc_s_f64(double %x) { ; CHECK-LABEL: i64_trunc_u_f64: ; CHECK-NEXT: .param f64{{$}} ; CHECK-NEXT: .result i64{{$}} -; CHECK-NEXT: i64.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: i64.trunc_u:sat/f64 $push[[NUM:[0-9]+]]=, $0{{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i64 @i64_trunc_u_f64(double %x) { %a = fptoui double %x to i64