mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-08 18:37:11 +00:00
[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
This commit is contained in:
parent
9e3381e8dc
commit
cdd48b8a6b
@ -61,8 +61,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);
|
||||
}
|
||||
|
||||
// For br_table instructions, encode the size of the table. In the MCInst,
|
||||
// there's an index operand, one operand for each table entry, and the
|
||||
|
@ -27,6 +27,10 @@ def FeatureSIMD128 : SubtargetFeature<"simd128", "HasSIMD128", "true",
|
||||
"Enable 128-bit SIMD">;
|
||||
def FeatureAtomics : SubtargetFeature<"atomics", "HasAtomics", "true",
|
||||
"Enable Atomics">;
|
||||
def FeatureNontrappingFPToInt :
|
||||
SubtargetFeature<"nontrapping-fptoint",
|
||||
"HasNontrappingFPToInt", "true",
|
||||
"Enable non-trapping float-to-int conversion operators">;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Architectures.
|
||||
|
@ -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"
|
||||
@ -184,6 +185,134 @@ 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 IConst = Int64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_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, Tmp2, Tmp3, Tmp4;
|
||||
Tmp0 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
|
||||
Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
|
||||
Tmp2 = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
|
||||
Tmp3 = MRI.createVirtualRegister(MRI.getRegClass(OutReg));
|
||||
Tmp4 = MRI.createVirtualRegister(MRI.getRegClass(OutReg));
|
||||
|
||||
MI.eraseFromParent();
|
||||
if (IsUnsigned) {
|
||||
Tmp0 = InReg;
|
||||
} else {
|
||||
BuildMI(BB, DL, TII.get(Abs), Tmp0)
|
||||
.addReg(InReg);
|
||||
}
|
||||
BuildMI(BB, DL, TII.get(FConst), Tmp1)
|
||||
.addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, CmpVal)));
|
||||
BuildMI(BB, DL, TII.get(LT), Tmp2)
|
||||
.addReg(Tmp0)
|
||||
.addReg(Tmp1);
|
||||
BuildMI(BB, DL, TII.get(WebAssembly::BR_IF))
|
||||
.addMBB(TrueMBB)
|
||||
.addReg(Tmp2);
|
||||
|
||||
BuildMI(FalseMBB, DL, TII.get(IConst), Tmp3)
|
||||
.addImm(Substitute);
|
||||
BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR))
|
||||
.addMBB(DoneMBB);
|
||||
BuildMI(TrueMBB, DL, TII.get(LoweredOpcode), Tmp4)
|
||||
.addReg(InReg);
|
||||
|
||||
BuildMI(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg)
|
||||
.addReg(Tmp3)
|
||||
.addMBB(FalseMBB)
|
||||
.addReg(Tmp4)
|
||||
.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<WebAssemblyISD::NodeType>(Opcode)) {
|
||||
|
@ -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<unsigned, const TargetRegisterClass *> getRegForInlineAsmConstraint(
|
||||
const TargetRegisterInfo *TRI, StringRef Constraint,
|
||||
|
@ -53,32 +53,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),
|
||||
|
@ -22,6 +22,14 @@ def HasSIMD128 : Predicate<"Subtarget->hasSIMD128()">,
|
||||
AssemblerPredicate<"FeatureSIMD128", "simd128">;
|
||||
def HasAtomics : Predicate<"Subtarget->hasAtomics()">,
|
||||
AssemblerPredicate<"FeatureAtomics", "atomics">;
|
||||
def HasNontrappingFPToInt :
|
||||
Predicate<"Subtarget->hasNontrappingFPToInt()">,
|
||||
AssemblerPredicate<"FeatureNontrappingFPToInt",
|
||||
"nontrapping-fptoint">;
|
||||
def NotHasNontrappingFPToInt :
|
||||
Predicate<"!Subtarget->hasNontrappingFPToInt()">,
|
||||
AssemblerPredicate<"!FeatureNontrappingFPToInt",
|
||||
"nontrapping-fptoint">;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// WebAssembly-specific DAG Node Types.
|
||||
|
@ -41,7 +41,8 @@ WebAssemblySubtarget::WebAssemblySubtarget(const Triple &TT,
|
||||
const std::string &FS,
|
||||
const TargetMachine &TM)
|
||||
: WebAssemblyGenSubtargetInfo(TT, CPU, FS), HasSIMD128(false),
|
||||
HasAtomics(false), CPUString(CPU), TargetTriple(TT), FrameLowering(),
|
||||
HasAtomics(false), HasNontrappingFPToInt(false), CPUString(CPU),
|
||||
TargetTriple(TT), FrameLowering(),
|
||||
InstrInfo(initializeSubtargetDependencies(FS)), TSInfo(),
|
||||
TLInfo(TM, *this) {}
|
||||
|
||||
|
@ -31,6 +31,7 @@ namespace llvm {
|
||||
class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
|
||||
bool HasSIMD128;
|
||||
bool HasAtomics;
|
||||
bool HasNontrappingFPToInt;
|
||||
|
||||
/// String name of used CPU.
|
||||
std::string CPUString;
|
||||
@ -76,6 +77,7 @@ public:
|
||||
bool hasAddr64() const { return TargetTriple.isArch64Bit(); }
|
||||
bool hasSIMD128() const { return HasSIMD128; }
|
||||
bool hasAtomics() const { return HasAtomics; }
|
||||
bool hasNontrappingFPToInt() const { return HasNontrappingFPToInt; }
|
||||
|
||||
/// Parses features string setting specified subtarget options. Definition of
|
||||
/// function is auto generated by tblgen.
|
||||
|
163
llvm/test/CodeGen/WebAssembly/conv-trap.ll
Normal file
163
llvm/test/CodeGen/WebAssembly/conv-trap.ll
Normal file
@ -0,0 +1,163 @@
|
||||
; 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: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
|
||||
; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
|
||||
; CHECK-NEXT: i32.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
; CHECK-NEXT: BB
|
||||
; CHECK-NEXT: end_block
|
||||
; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, -2147483648{{$}}
|
||||
; CHECK-NEXT: return $pop[[ALT]]{{$}}
|
||||
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: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
|
||||
; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
|
||||
; CHECK-NEXT: i32.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
; CHECK-NEXT: BB
|
||||
; CHECK-NEXT: end_block
|
||||
; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, 0{{$}}
|
||||
; CHECK-NEXT: return $pop[[ALT]]{{$}}
|
||||
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: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
|
||||
; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
|
||||
; CHECK-NEXT: i32.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
; CHECK-NEXT: BB
|
||||
; CHECK-NEXT: end_block
|
||||
; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, -2147483648{{$}}
|
||||
; CHECK-NEXT: return $pop[[ALT]]{{$}}
|
||||
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: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
|
||||
; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
|
||||
; CHECK-NEXT: i32.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
; CHECK-NEXT: BB
|
||||
; CHECK-NEXT: end_block
|
||||
; CHECK-NEXT: i32.const $push[[ALT:[0-9]+]]=, 0{{$}}
|
||||
; CHECK-NEXT: return $pop[[ALT]]{{$}}
|
||||
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: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
|
||||
; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
|
||||
; CHECK-NEXT: i64.trunc_s/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
; CHECK-NEXT: BB
|
||||
; CHECK-NEXT: end_block
|
||||
; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, -9223372036854775808{{$}}
|
||||
; CHECK-NEXT: return $pop[[ALT]]{{$}}
|
||||
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: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
|
||||
; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
|
||||
; CHECK-NEXT: i64.trunc_u/f32 $push[[NUM:[0-9]+]]=, $0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
; CHECK-NEXT: BB
|
||||
; CHECK-NEXT: end_block
|
||||
; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, 0{{$}}
|
||||
; CHECK-NEXT: return $pop[[ALT]]{{$}}
|
||||
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: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
|
||||
; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
|
||||
; CHECK-NEXT: i64.trunc_s/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
; CHECK-NEXT: BB
|
||||
; CHECK-NEXT: end_block
|
||||
; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, -9223372036854775808{{$}}
|
||||
; CHECK-NEXT: return $pop[[ALT]]{{$}}
|
||||
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: i32.eqz $push[[EQZ:[0-9]+]]=, $pop[[LT]]{{$}}
|
||||
; CHECK-NEXT: br_if 0, $pop[[EQZ]]{{$}}
|
||||
; CHECK-NEXT: i64.trunc_u/f64 $push[[NUM:[0-9]+]]=, $0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
; CHECK-NEXT: BB
|
||||
; CHECK-NEXT: end_block
|
||||
; CHECK-NEXT: i64.const $push[[ALT:[0-9]+]]=, 0{{$}}
|
||||
; CHECK-NEXT: return $pop[[ALT]]{{$}}
|
||||
define i64 @i64_trunc_u_f64(double %x) {
|
||||
%a = fptoui double %x to i64
|
||||
ret i64 %a
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals | FileCheck %s
|
||||
; 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.
|
||||
|
||||
@ -38,7 +38,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 +48,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 +58,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 +68,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 +78,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 +88,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 +98,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 +108,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
|
||||
|
Loading…
x
Reference in New Issue
Block a user