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

Backporting wasm cast fixes #99

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions lib/Target/WebAssembly/WebAssembly.td
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
155 changes: 155 additions & 0 deletions lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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>(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>(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<WebAssemblyISD::NodeType>(Opcode)) {
Expand Down
3 changes: 3 additions & 0 deletions lib/Target/WebAssembly/WebAssemblyISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
88 changes: 72 additions & 16 deletions lib/Target/WebAssembly/WebAssemblyInstrConv.td
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
8 changes: 8 additions & 0 deletions lib/Target/WebAssembly/WebAssemblyInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions lib/Target/WebAssembly/WebAssemblyLowerBrUnless.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
3 changes: 2 additions & 1 deletion lib/Target/WebAssembly/WebAssemblySubtarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}

Expand Down
2 changes: 2 additions & 0 deletions lib/Target/WebAssembly/WebAssemblySubtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace llvm {

class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
bool HasSIMD128;
bool HasNontrappingFPToInt;

/// String name of used CPU.
std::string CPUString;
Expand Down Expand Up @@ -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.
Expand Down
Loading