mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-24 12:20:00 +00:00
[WebAssembly] Implement unaligned loads and stores.
Differential Revision: http://reviews.llvm.org/D16534 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@258779 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
33a530fbed
commit
407037aa3d
@ -29,6 +29,7 @@ add_llvm_target(WebAssemblyCodeGen
|
||||
WebAssemblyRegNumbering.cpp
|
||||
WebAssemblyRegStackify.cpp
|
||||
WebAssemblySelectionDAGInfo.cpp
|
||||
WebAssemblySetP2AlignOperands.cpp
|
||||
WebAssemblyStoreResults.cpp
|
||||
WebAssemblySubtarget.cpp
|
||||
WebAssemblyTargetMachine.cpp
|
||||
|
@ -93,6 +93,7 @@ MCDisassembler::DecodeStatus WebAssemblyDisassembler::getInstruction(
|
||||
const MCOperandInfo &Info = Desc.OpInfo[i];
|
||||
switch (Info.OperandType) {
|
||||
case MCOI::OPERAND_IMMEDIATE:
|
||||
case WebAssembly::OPERAND_P2ALIGN:
|
||||
case WebAssembly::OPERAND_BASIC_BLOCK: {
|
||||
if (Pos + sizeof(uint64_t) > Bytes.size())
|
||||
return MCDisassembler::Fail;
|
||||
|
@ -172,6 +172,16 @@ void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand(const MCInst *MI,
|
||||
unsigned OpNo,
|
||||
raw_ostream &O) {
|
||||
int64_t Imm = MI->getOperand(OpNo).getImm();
|
||||
if (Imm == WebAssembly::GetDefaultP2Align(MI->getOpcode()))
|
||||
return;
|
||||
O << ":p2align=" << Imm;
|
||||
}
|
||||
|
||||
const char *llvm::WebAssembly::TypeToString(MVT Ty) {
|
||||
switch (Ty.SimpleTy) {
|
||||
case MVT::i32:
|
||||
|
@ -36,6 +36,8 @@ public:
|
||||
|
||||
// Used by tblegen code.
|
||||
void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O);
|
||||
void printWebAssemblyP2AlignOperand(const MCInst *MI, unsigned OpNo,
|
||||
raw_ostream &O);
|
||||
|
||||
// Autogenerated by tblgen.
|
||||
void printInstruction(const MCInst *MI, raw_ostream &O);
|
||||
|
@ -45,7 +45,9 @@ enum OperandType {
|
||||
/// Basic block label in a branch construct.
|
||||
OPERAND_BASIC_BLOCK = MCOI::OPERAND_FIRST_TARGET,
|
||||
/// Floating-point immediate.
|
||||
OPERAND_FPIMM
|
||||
OPERAND_FPIMM,
|
||||
/// p2align immediate for load and store address alignment.
|
||||
OPERAND_P2ALIGN
|
||||
};
|
||||
|
||||
/// WebAssembly-specific directive identifiers.
|
||||
@ -86,4 +88,47 @@ enum {
|
||||
#define GET_SUBTARGETINFO_ENUM
|
||||
#include "WebAssemblyGenSubtargetInfo.inc"
|
||||
|
||||
namespace llvm {
|
||||
namespace WebAssembly {
|
||||
|
||||
/// Return the default p2align value for a load or store with the given opcode.
|
||||
inline unsigned GetDefaultP2Align(unsigned Opcode) {
|
||||
switch (Opcode) {
|
||||
case WebAssembly::LOAD8_S_I32:
|
||||
case WebAssembly::LOAD8_U_I32:
|
||||
case WebAssembly::LOAD8_S_I64:
|
||||
case WebAssembly::LOAD8_U_I64:
|
||||
case WebAssembly::STORE8_I32:
|
||||
case WebAssembly::STORE8_I64:
|
||||
return 0;
|
||||
case WebAssembly::LOAD16_S_I32:
|
||||
case WebAssembly::LOAD16_U_I32:
|
||||
case WebAssembly::LOAD16_S_I64:
|
||||
case WebAssembly::LOAD16_U_I64:
|
||||
case WebAssembly::STORE16_I32:
|
||||
case WebAssembly::STORE16_I64:
|
||||
return 1;
|
||||
case WebAssembly::LOAD_I32:
|
||||
case WebAssembly::LOAD_F32:
|
||||
case WebAssembly::STORE_I32:
|
||||
case WebAssembly::STORE_F32:
|
||||
case WebAssembly::LOAD32_S_I64:
|
||||
case WebAssembly::LOAD32_U_I64:
|
||||
case WebAssembly::STORE32_I64:
|
||||
return 2;
|
||||
case WebAssembly::LOAD_I64:
|
||||
case WebAssembly::LOAD_F64:
|
||||
case WebAssembly::STORE_I64:
|
||||
case WebAssembly::STORE_F64:
|
||||
return 3;
|
||||
default: llvm_unreachable("Only loads and stores have p2align values");
|
||||
}
|
||||
}
|
||||
|
||||
/// The operand number of the stored value in a store instruction.
|
||||
static const unsigned StoreValueOperandNo = 4;
|
||||
|
||||
} // end namespace WebAssembly
|
||||
} // end namespace llvm
|
||||
|
||||
#endif
|
||||
|
@ -28,6 +28,7 @@ FunctionPass *createWebAssemblyOptimizeReturned();
|
||||
FunctionPass *createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,
|
||||
CodeGenOpt::Level OptLevel);
|
||||
FunctionPass *createWebAssemblyArgumentMove();
|
||||
FunctionPass *createWebAssemblySetP2AlignOperands();
|
||||
|
||||
FunctionPass *createWebAssemblyStoreResults();
|
||||
FunctionPass *createWebAssemblyRegStackify();
|
||||
|
@ -84,6 +84,7 @@ static void adjustStackPointer(unsigned StackSize,
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::LOAD_I32), SPReg)
|
||||
.addImm(0)
|
||||
.addReg(SPReg)
|
||||
.addImm(2) // p2align
|
||||
.addMemOperand(LoadMMO);
|
||||
// Add/Subtract the frame size
|
||||
unsigned OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
|
||||
@ -102,6 +103,7 @@ static void adjustStackPointer(unsigned StackSize,
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::STORE_I32), WebAssembly::SP32)
|
||||
.addImm(0)
|
||||
.addReg(OffsetReg)
|
||||
.addImm(2) // p2align
|
||||
.addReg(WebAssembly::SP32)
|
||||
.addMemOperand(MMO);
|
||||
}
|
||||
@ -169,6 +171,7 @@ void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::STORE_I32), WebAssembly::SP32)
|
||||
.addImm(0)
|
||||
.addReg(OffsetReg)
|
||||
.addImm(2) // p2align
|
||||
.addReg(WebAssembly::SP32)
|
||||
.addMemOperand(MMO);
|
||||
}
|
||||
|
@ -285,6 +285,20 @@ bool WebAssemblyTargetLowering::isLegalAddressingMode(const DataLayout &DL,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebAssemblyTargetLowering::allowsMisalignedMemoryAccesses(
|
||||
EVT /*VT*/, unsigned /*AddrSpace*/, unsigned /*Align*/,
|
||||
bool *Fast) const {
|
||||
// WebAssembly supports unaligned accesses, though it should be declared
|
||||
// with the p2align attribute on loads and stores which do so, and there
|
||||
// may be a performance impact. We tell LLVM they're "fast" because
|
||||
// for the kinds of things that LLVM uses this for (merging agacent stores
|
||||
// of constants, etc.), WebAssembly implementations will either want the
|
||||
// unaligned access or they'll split anyway.
|
||||
if (Fast)
|
||||
*Fast = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// WebAssembly Lowering private implementation.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -56,6 +56,8 @@ private:
|
||||
bool isCheapToSpeculateCtlz() const override;
|
||||
bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty,
|
||||
unsigned AS) const override;
|
||||
bool allowsMisalignedMemoryAccesses(EVT, unsigned AddrSpace, unsigned Align,
|
||||
bool *Fast) const override;
|
||||
|
||||
SDValue LowerCall(CallLoweringInfo &CLI,
|
||||
SmallVectorImpl<SDValue> &InVals) const override;
|
||||
|
@ -76,6 +76,12 @@ def f32imm_op : Operand<f32>;
|
||||
def f64imm_op : Operand<f64>;
|
||||
} // OperandType = "OPERAND_FPIMM"
|
||||
|
||||
let OperandType = "OPERAND_P2ALIGN" in {
|
||||
def P2Align : Operand<i32> {
|
||||
let PrintMethod = "printWebAssemblyP2AlignOperand";
|
||||
}
|
||||
} // OperandType = "OPERAND_P2ALIGN"
|
||||
|
||||
} // OperandNamespace = "WebAssembly"
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -46,325 +46,354 @@ def regPlusGA : PatFrag<(ops node:$addr, node:$off),
|
||||
let Defs = [ARGUMENTS] in {
|
||||
|
||||
// Basic load.
|
||||
def LOAD_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i32.load\t$dst, ${off}(${addr})">;
|
||||
def LOAD_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i64.load\t$dst, ${off}(${addr})">;
|
||||
def LOAD_F32 : I<(outs F32:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"f32.load\t$dst, ${off}(${addr})">;
|
||||
def LOAD_F64 : I<(outs F64:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"f64.load\t$dst, ${off}(${addr})">;
|
||||
def LOAD_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i32.load\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i64.load\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD_F32 : I<(outs F32:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"f32.load\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD_F64 : I<(outs F64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"f64.load\t$dst, ${off}(${addr})${p2align}">;
|
||||
|
||||
} // Defs = [ARGUMENTS]
|
||||
|
||||
// Select loads with no constant offset.
|
||||
def : Pat<(i32 (load I32:$addr)), (LOAD_I32 0, $addr)>;
|
||||
def : Pat<(i64 (load I32:$addr)), (LOAD_I64 0, $addr)>;
|
||||
def : Pat<(f32 (load I32:$addr)), (LOAD_F32 0, $addr)>;
|
||||
def : Pat<(f64 (load I32:$addr)), (LOAD_F64 0, $addr)>;
|
||||
def : Pat<(i32 (load I32:$addr)), (LOAD_I32 0, $addr, 0)>;
|
||||
def : Pat<(i64 (load I32:$addr)), (LOAD_I64 0, $addr, 0)>;
|
||||
def : Pat<(f32 (load I32:$addr)), (LOAD_F32 0, $addr, 0)>;
|
||||
def : Pat<(f64 (load I32:$addr)), (LOAD_F64 0, $addr, 0)>;
|
||||
|
||||
// Select loads with a constant offset.
|
||||
def : Pat<(i32 (load (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD_I32 imm:$off, $addr)>;
|
||||
(LOAD_I32 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (load (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD_I64 imm:$off, $addr)>;
|
||||
(LOAD_I64 imm:$off, $addr, 0)>;
|
||||
def : Pat<(f32 (load (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD_F32 imm:$off, $addr)>;
|
||||
(LOAD_F32 imm:$off, $addr, 0)>;
|
||||
def : Pat<(f64 (load (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD_F64 imm:$off, $addr)>;
|
||||
(LOAD_F64 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (load (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD_I32 tglobaladdr:$off, $addr)>;
|
||||
(LOAD_I32 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (load (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD_I64 tglobaladdr:$off, $addr)>;
|
||||
(LOAD_I64 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(f32 (load (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD_F32 tglobaladdr:$off, $addr)>;
|
||||
(LOAD_F32 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(f64 (load (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD_F64 tglobaladdr:$off, $addr)>;
|
||||
(LOAD_F64 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (load (add I32:$addr, (WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD_I32 texternalsym:$off, $addr)>;
|
||||
(LOAD_I32 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (load (add I32:$addr, (WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD_I64 texternalsym:$off, $addr)>;
|
||||
(LOAD_I64 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(f32 (load (add I32:$addr, (WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD_F32 texternalsym:$off, $addr)>;
|
||||
(LOAD_F32 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(f64 (load (add I32:$addr, (WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD_F64 texternalsym:$off, $addr)>;
|
||||
(LOAD_F64 texternalsym:$off, $addr, 0)>;
|
||||
|
||||
// Select loads with just a constant offset.
|
||||
def : Pat<(i32 (load imm:$off)), (LOAD_I32 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i64 (load imm:$off)), (LOAD_I64 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(f32 (load imm:$off)), (LOAD_F32 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(f64 (load imm:$off)), (LOAD_F64 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i32 (load imm:$off)), (LOAD_I32 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (load imm:$off)), (LOAD_I64 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(f32 (load imm:$off)), (LOAD_F32 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(f64 (load imm:$off)), (LOAD_F64 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (load (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD_I32 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD_I32 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (load (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD_I64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD_I64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(f32 (load (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD_F32 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD_F32 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(f64 (load (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD_F64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD_F64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (load (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD_I32 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD_I32 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (load (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD_I64 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD_I64 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(f32 (load (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD_F32 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD_F32 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(f64 (load (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD_F64 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD_F64 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
|
||||
let Defs = [ARGUMENTS] in {
|
||||
|
||||
// Extending load.
|
||||
def LOAD8_S_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i32.load8_s\t$dst, ${off}(${addr})">;
|
||||
def LOAD8_U_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i32.load8_u\t$dst, ${off}(${addr})">;
|
||||
def LOAD16_S_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i32.load16_s\t$dst, ${off}(${addr})">;
|
||||
def LOAD16_U_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i32.load16_u\t$dst, ${off}(${addr})">;
|
||||
def LOAD8_S_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i64.load8_s\t$dst, ${off}(${addr})">;
|
||||
def LOAD8_U_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i64.load8_u\t$dst, ${off}(${addr})">;
|
||||
def LOAD16_S_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i64.load16_s\t$dst, ${off}(${addr})">;
|
||||
def LOAD16_U_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i64.load16_u\t$dst, ${off}(${addr})">;
|
||||
def LOAD32_S_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i64.load32_s\t$dst, ${off}(${addr})">;
|
||||
def LOAD32_U_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr), [],
|
||||
"i64.load32_u\t$dst, ${off}(${addr})">;
|
||||
def LOAD8_S_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i32.load8_s\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD8_U_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i32.load8_u\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD16_S_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i32.load16_s\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD16_U_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i32.load16_u\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD8_S_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i64.load8_s\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD8_U_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i64.load8_u\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD16_S_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i64.load16_s\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD16_U_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i64.load16_u\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD32_S_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i64.load32_s\t$dst, ${off}(${addr})${p2align}">;
|
||||
def LOAD32_U_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align), [],
|
||||
"i64.load32_u\t$dst, ${off}(${addr})${p2align}">;
|
||||
|
||||
} // Defs = [ARGUMENTS]
|
||||
|
||||
// Select extending loads with no constant offset.
|
||||
def : Pat<(i32 (sextloadi8 I32:$addr)), (LOAD8_S_I32 0, $addr)>;
|
||||
def : Pat<(i32 (zextloadi8 I32:$addr)), (LOAD8_U_I32 0, $addr)>;
|
||||
def : Pat<(i32 (sextloadi16 I32:$addr)), (LOAD16_S_I32 0, $addr)>;
|
||||
def : Pat<(i32 (zextloadi16 I32:$addr)), (LOAD16_U_I32 0, $addr)>;
|
||||
def : Pat<(i64 (sextloadi8 I32:$addr)), (LOAD8_S_I64 0, $addr)>;
|
||||
def : Pat<(i64 (zextloadi8 I32:$addr)), (LOAD8_U_I64 0, $addr)>;
|
||||
def : Pat<(i64 (sextloadi16 I32:$addr)), (LOAD16_S_I64 0, $addr)>;
|
||||
def : Pat<(i64 (zextloadi16 I32:$addr)), (LOAD16_U_I64 0, $addr)>;
|
||||
def : Pat<(i64 (sextloadi32 I32:$addr)), (LOAD32_S_I64 0, $addr)>;
|
||||
def : Pat<(i64 (zextloadi32 I32:$addr)), (LOAD32_U_I64 0, $addr)>;
|
||||
def : Pat<(i32 (sextloadi8 I32:$addr)), (LOAD8_S_I32 0, $addr, 0)>;
|
||||
def : Pat<(i32 (zextloadi8 I32:$addr)), (LOAD8_U_I32 0, $addr, 0)>;
|
||||
def : Pat<(i32 (sextloadi16 I32:$addr)), (LOAD16_S_I32 0, $addr, 0)>;
|
||||
def : Pat<(i32 (zextloadi16 I32:$addr)), (LOAD16_U_I32 0, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi8 I32:$addr)), (LOAD8_S_I64 0, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi8 I32:$addr)), (LOAD8_U_I64 0, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi16 I32:$addr)), (LOAD16_S_I64 0, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi16 I32:$addr)), (LOAD16_U_I64 0, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi32 I32:$addr)), (LOAD32_S_I64 0, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi32 I32:$addr)), (LOAD32_U_I64 0, $addr, 0)>;
|
||||
|
||||
// Select extending loads with a constant offset.
|
||||
def : Pat<(i32 (sextloadi8 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD8_S_I32 imm:$off, $addr)>;
|
||||
(LOAD8_S_I32 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (zextloadi8 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD8_U_I32 imm:$off, $addr)>;
|
||||
(LOAD8_U_I32 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (sextloadi16 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD16_S_I32 imm:$off, $addr)>;
|
||||
(LOAD16_S_I32 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (zextloadi16 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD16_U_I32 imm:$off, $addr)>;
|
||||
(LOAD16_U_I32 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi8 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD8_S_I64 imm:$off, $addr)>;
|
||||
(LOAD8_S_I64 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi8 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD8_U_I64 imm:$off, $addr)>;
|
||||
(LOAD8_U_I64 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi16 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD16_S_I64 imm:$off, $addr)>;
|
||||
(LOAD16_S_I64 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi16 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD16_U_I64 imm:$off, $addr)>;
|
||||
(LOAD16_U_I64 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi32 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD32_S_I64 imm:$off, $addr)>;
|
||||
(LOAD32_S_I64 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi32 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD32_U_I64 imm:$off, $addr)>;
|
||||
(LOAD32_U_I64 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (sextloadi8 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD8_S_I32 tglobaladdr:$off, $addr)>;
|
||||
(LOAD8_S_I32 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (zextloadi8 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD8_U_I32 tglobaladdr:$off, $addr)>;
|
||||
(LOAD8_U_I32 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (sextloadi16 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD16_S_I32 tglobaladdr:$off, $addr)>;
|
||||
(LOAD16_S_I32 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (zextloadi16 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD16_U_I32 tglobaladdr:$off, $addr)>;
|
||||
(LOAD16_U_I32 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi8 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD8_S_I64 tglobaladdr:$off, $addr)>;
|
||||
(LOAD8_S_I64 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi8 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD8_U_I64 tglobaladdr:$off, $addr)>;
|
||||
(LOAD8_U_I64 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi16 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD16_S_I64 tglobaladdr:$off, $addr)>;
|
||||
(LOAD16_S_I64 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi16 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD16_U_I64 tglobaladdr:$off, $addr)>;
|
||||
(LOAD16_U_I64 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi32 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD32_S_I64 tglobaladdr:$off, $addr)>;
|
||||
(LOAD32_S_I64 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi32 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD32_U_I64 tglobaladdr:$off, $addr)>;
|
||||
(LOAD32_U_I64 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (sextloadi8 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD8_S_I32 texternalsym:$off, $addr)>;
|
||||
(LOAD8_S_I32 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (zextloadi8 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD8_U_I32 texternalsym:$off, $addr)>;
|
||||
(LOAD8_U_I32 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (sextloadi16 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD16_S_I32 texternalsym:$off, $addr)>;
|
||||
(LOAD16_S_I32 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (zextloadi16 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD16_U_I32 texternalsym:$off, $addr)>;
|
||||
(LOAD16_U_I32 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi8 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD8_S_I64 texternalsym:$off, $addr)>;
|
||||
(LOAD8_S_I64 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi8 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD8_U_I64 texternalsym:$off, $addr)>;
|
||||
(LOAD8_U_I64 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi16 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD16_S_I64 texternalsym:$off, $addr)>;
|
||||
(LOAD16_S_I64 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi16 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD16_U_I64 texternalsym:$off, $addr)>;
|
||||
(LOAD16_U_I64 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (sextloadi32 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD32_S_I64 texternalsym:$off, $addr)>;
|
||||
(LOAD32_S_I64 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (zextloadi32 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD32_U_I64 texternalsym:$off, $addr)>;
|
||||
(LOAD32_U_I64 texternalsym:$off, $addr, 0)>;
|
||||
|
||||
// Select extending loads with just a constant offset.
|
||||
def : Pat<(i32 (sextloadi8 imm:$off)), (LOAD8_S_I32 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i32 (zextloadi8 imm:$off)), (LOAD8_U_I32 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i32 (sextloadi16 imm:$off)), (LOAD16_S_I32 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i32 (zextloadi16 imm:$off)), (LOAD16_U_I32 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i64 (sextloadi8 imm:$off)), (LOAD8_S_I64 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i64 (zextloadi8 imm:$off)), (LOAD8_U_I64 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i64 (sextloadi16 imm:$off)), (LOAD16_S_I64 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i64 (zextloadi16 imm:$off)), (LOAD16_U_I64 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i64 (sextloadi32 imm:$off)), (LOAD32_S_I64 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i64 (zextloadi32 imm:$off)), (LOAD32_U_I64 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i32 (sextloadi8 imm:$off)),
|
||||
(LOAD8_S_I32 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (zextloadi8 imm:$off)),
|
||||
(LOAD8_U_I32 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (sextloadi16 imm:$off)),
|
||||
(LOAD16_S_I32 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (zextloadi16 imm:$off)),
|
||||
(LOAD16_U_I32 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (sextloadi8 imm:$off)),
|
||||
(LOAD8_S_I64 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (zextloadi8 imm:$off)),
|
||||
(LOAD8_U_I64 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (sextloadi16 imm:$off)),
|
||||
(LOAD16_S_I64 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (zextloadi16 imm:$off)),
|
||||
(LOAD16_U_I64 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (sextloadi32 imm:$off)),
|
||||
(LOAD32_S_I64 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (zextloadi32 imm:$off)),
|
||||
(LOAD32_U_I64 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (sextloadi8 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD8_S_I32 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD8_S_I32 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (zextloadi8 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD8_U_I32 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD8_U_I32 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (sextloadi16 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD16_S_I32 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD16_S_I32 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (zextloadi16 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD16_U_I32 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD16_U_I32 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (sextloadi8 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD8_S_I64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD8_S_I64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (zextloadi8 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD8_U_I64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD8_U_I64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (sextloadi16 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD16_S_I64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD16_S_I64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (zextloadi16 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD16_U_I64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD16_U_I64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (sextloadi32 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD32_S_I64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD32_S_I64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (zextloadi32 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD32_U_I64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD32_U_I64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (sextloadi8 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD8_S_I32 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD8_S_I32 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (zextloadi8 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD8_U_I32 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD8_U_I32 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (sextloadi16 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD16_S_I32 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD16_S_I32 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (zextloadi16 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD16_U_I32 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD16_U_I32 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (sextloadi8 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD8_S_I64 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD8_S_I64 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (zextloadi8 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD8_U_I64 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD8_U_I64 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (sextloadi16 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD16_S_I64 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD16_S_I64 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (zextloadi16 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD16_U_I64 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD16_U_I64 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (sextloadi32 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD32_S_I64 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD32_S_I64 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (zextloadi32 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD32_U_I64 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD32_U_I64 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
|
||||
// Resolve "don't care" extending loads to zero-extending loads. This is
|
||||
// somewhat arbitrary, but zero-extending is conceptually simpler.
|
||||
|
||||
// Select "don't care" extending loads with no constant offset.
|
||||
def : Pat<(i32 (extloadi8 I32:$addr)), (LOAD8_U_I32 0, $addr)>;
|
||||
def : Pat<(i32 (extloadi16 I32:$addr)), (LOAD16_U_I32 0, $addr)>;
|
||||
def : Pat<(i64 (extloadi8 I32:$addr)), (LOAD8_U_I64 0, $addr)>;
|
||||
def : Pat<(i64 (extloadi16 I32:$addr)), (LOAD16_U_I64 0, $addr)>;
|
||||
def : Pat<(i64 (extloadi32 I32:$addr)), (LOAD32_U_I64 0, $addr)>;
|
||||
def : Pat<(i32 (extloadi8 I32:$addr)), (LOAD8_U_I32 0, $addr, 0)>;
|
||||
def : Pat<(i32 (extloadi16 I32:$addr)), (LOAD16_U_I32 0, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi8 I32:$addr)), (LOAD8_U_I64 0, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi16 I32:$addr)), (LOAD16_U_I64 0, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi32 I32:$addr)), (LOAD32_U_I64 0, $addr, 0)>;
|
||||
|
||||
// Select "don't care" extending loads with a constant offset.
|
||||
def : Pat<(i32 (extloadi8 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD8_U_I32 imm:$off, $addr)>;
|
||||
(LOAD8_U_I32 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (extloadi16 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD16_U_I32 imm:$off, $addr)>;
|
||||
(LOAD16_U_I32 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi8 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD8_U_I64 imm:$off, $addr)>;
|
||||
(LOAD8_U_I64 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi16 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD16_U_I64 imm:$off, $addr)>;
|
||||
(LOAD16_U_I64 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi32 (regPlusImm I32:$addr, imm:$off))),
|
||||
(LOAD32_U_I64 imm:$off, $addr)>;
|
||||
(LOAD32_U_I64 imm:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (extloadi8 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD8_U_I32 tglobaladdr:$off, $addr)>;
|
||||
(LOAD8_U_I32 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (extloadi16 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD16_U_I32 tglobaladdr:$off, $addr)>;
|
||||
(LOAD16_U_I32 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi8 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD8_U_I64 tglobaladdr:$off, $addr)>;
|
||||
(LOAD8_U_I64 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi16 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD16_U_I64 tglobaladdr:$off, $addr)>;
|
||||
(LOAD16_U_I64 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi32 (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off)))),
|
||||
(LOAD32_U_I64 tglobaladdr:$off, $addr)>;
|
||||
(LOAD32_U_I64 tglobaladdr:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (extloadi8 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD8_U_I32 texternalsym:$off, $addr)>;
|
||||
(LOAD8_U_I32 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i32 (extloadi16 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD16_U_I32 texternalsym:$off, $addr)>;
|
||||
(LOAD16_U_I32 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi8 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD8_U_I64 texternalsym:$off, $addr)>;
|
||||
(LOAD8_U_I64 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi16 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD16_U_I64 texternalsym:$off, $addr)>;
|
||||
(LOAD16_U_I64 texternalsym:$off, $addr, 0)>;
|
||||
def : Pat<(i64 (extloadi32 (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off)))),
|
||||
(LOAD32_U_I64 texternalsym:$off, $addr)>;
|
||||
(LOAD32_U_I64 texternalsym:$off, $addr, 0)>;
|
||||
|
||||
// Select "don't care" extending loads with just a constant offset.
|
||||
def : Pat<(i32 (extloadi8 imm:$off)), (LOAD8_U_I32 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i32 (extloadi16 imm:$off)), (LOAD16_U_I32 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i64 (extloadi8 imm:$off)), (LOAD8_U_I64 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i64 (extloadi16 imm:$off)), (LOAD16_U_I64 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i64 (extloadi32 imm:$off)), (LOAD32_U_I64 imm:$off, (CONST_I32 0))>;
|
||||
def : Pat<(i32 (extloadi8 imm:$off)),
|
||||
(LOAD8_U_I32 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (extloadi16 imm:$off)),
|
||||
(LOAD16_U_I32 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (extloadi8 imm:$off)),
|
||||
(LOAD8_U_I64 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (extloadi16 imm:$off)),
|
||||
(LOAD16_U_I64 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (extloadi32 imm:$off)),
|
||||
(LOAD32_U_I64 imm:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (extloadi8 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD8_U_I32 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD8_U_I32 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (extloadi16 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD16_U_I32 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD16_U_I32 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (extloadi8 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD8_U_I64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD8_U_I64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (extloadi16 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD16_U_I64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD16_U_I64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (extloadi32 (WebAssemblywrapper tglobaladdr:$off))),
|
||||
(LOAD32_U_I64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD32_U_I64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (extloadi8 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD8_U_I32 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD8_U_I32 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i32 (extloadi16 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD16_U_I32 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD16_U_I32 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (extloadi8 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD8_U_I64 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD8_U_I64 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (extloadi16 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD16_U_I64 texternalsym:$off, (CONST_I32 0))>;
|
||||
(LOAD16_U_I64 texternalsym:$off, (CONST_I32 0), 0)>;
|
||||
def : Pat<(i64 (extloadi32 (WebAssemblywrapper texternalsym:$off))),
|
||||
(LOAD32_U_I64 tglobaladdr:$off, (CONST_I32 0))>;
|
||||
(LOAD32_U_I64 tglobaladdr:$off, (CONST_I32 0), 0)>;
|
||||
|
||||
let Defs = [ARGUMENTS] in {
|
||||
|
||||
@ -374,193 +403,202 @@ let Defs = [ARGUMENTS] in {
|
||||
// instruction definition patterns that don't reference all of the output
|
||||
// operands.
|
||||
// Note: WebAssembly inverts SelectionDAG's usual operand order.
|
||||
def STORE_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr, I32:$val), [],
|
||||
"i32.store\t$dst, ${off}(${addr}), $val">;
|
||||
def STORE_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr, I64:$val), [],
|
||||
"i64.store\t$dst, ${off}(${addr}), $val">;
|
||||
def STORE_F32 : I<(outs F32:$dst), (ins i32imm:$off, I32:$addr, F32:$val), [],
|
||||
"f32.store\t$dst, ${off}(${addr}), $val">;
|
||||
def STORE_F64 : I<(outs F64:$dst), (ins i32imm:$off, I32:$addr, F64:$val), [],
|
||||
"f64.store\t$dst, ${off}(${addr}), $val">;
|
||||
def STORE_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align, I32:$val), [],
|
||||
"i32.store\t$dst, ${off}(${addr})${p2align}, $val">;
|
||||
def STORE_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align, I64:$val), [],
|
||||
"i64.store\t$dst, ${off}(${addr})${p2align}, $val">;
|
||||
def STORE_F32 : I<(outs F32:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align, F32:$val), [],
|
||||
"f32.store\t$dst, ${off}(${addr})${p2align}, $val">;
|
||||
def STORE_F64 : I<(outs F64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align, F64:$val), [],
|
||||
"f64.store\t$dst, ${off}(${addr})${p2align}, $val">;
|
||||
|
||||
} // Defs = [ARGUMENTS]
|
||||
|
||||
// Select stores with no constant offset.
|
||||
def : Pat<(store I32:$val, I32:$addr), (STORE_I32 0, I32:$addr, I32:$val)>;
|
||||
def : Pat<(store I64:$val, I32:$addr), (STORE_I64 0, I32:$addr, I64:$val)>;
|
||||
def : Pat<(store F32:$val, I32:$addr), (STORE_F32 0, I32:$addr, F32:$val)>;
|
||||
def : Pat<(store F64:$val, I32:$addr), (STORE_F64 0, I32:$addr, F64:$val)>;
|
||||
def : Pat<(store I32:$val, I32:$addr), (STORE_I32 0, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(store I64:$val, I32:$addr), (STORE_I64 0, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(store F32:$val, I32:$addr), (STORE_F32 0, I32:$addr, 0, F32:$val)>;
|
||||
def : Pat<(store F64:$val, I32:$addr), (STORE_F64 0, I32:$addr, 0, F64:$val)>;
|
||||
|
||||
// Select stores with a constant offset.
|
||||
def : Pat<(store I32:$val, (regPlusImm I32:$addr, imm:$off)),
|
||||
(STORE_I32 imm:$off, I32:$addr, I32:$val)>;
|
||||
(STORE_I32 imm:$off, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(store I64:$val, (regPlusImm I32:$addr, imm:$off)),
|
||||
(STORE_I64 imm:$off, I32:$addr, I64:$val)>;
|
||||
(STORE_I64 imm:$off, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(store F32:$val, (regPlusImm I32:$addr, imm:$off)),
|
||||
(STORE_F32 imm:$off, I32:$addr, F32:$val)>;
|
||||
(STORE_F32 imm:$off, I32:$addr, 0, F32:$val)>;
|
||||
def : Pat<(store F64:$val, (regPlusImm I32:$addr, imm:$off)),
|
||||
(STORE_F64 imm:$off, I32:$addr, F64:$val)>;
|
||||
(STORE_F64 imm:$off, I32:$addr, 0, F64:$val)>;
|
||||
def : Pat<(store I32:$val, (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off))),
|
||||
(STORE_I32 tglobaladdr:$off, I32:$addr, I32:$val)>;
|
||||
(STORE_I32 tglobaladdr:$off, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(store I64:$val, (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off))),
|
||||
(STORE_I64 tglobaladdr:$off, I32:$addr, I64:$val)>;
|
||||
(STORE_I64 tglobaladdr:$off, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(store F32:$val, (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off))),
|
||||
(STORE_F32 tglobaladdr:$off, I32:$addr, F32:$val)>;
|
||||
(STORE_F32 tglobaladdr:$off, I32:$addr, 0, F32:$val)>;
|
||||
def : Pat<(store F64:$val, (regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off))),
|
||||
(STORE_F64 tglobaladdr:$off, I32:$addr, F64:$val)>;
|
||||
(STORE_F64 tglobaladdr:$off, I32:$addr, 0, F64:$val)>;
|
||||
def : Pat<(store I32:$val, (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off))),
|
||||
(STORE_I32 texternalsym:$off, I32:$addr, I32:$val)>;
|
||||
(STORE_I32 texternalsym:$off, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(store I64:$val, (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off))),
|
||||
(STORE_I64 texternalsym:$off, I32:$addr, I64:$val)>;
|
||||
(STORE_I64 texternalsym:$off, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(store F32:$val, (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off))),
|
||||
(STORE_F32 texternalsym:$off, I32:$addr, F32:$val)>;
|
||||
(STORE_F32 texternalsym:$off, I32:$addr, 0, F32:$val)>;
|
||||
def : Pat<(store F64:$val, (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off))),
|
||||
(STORE_F64 texternalsym:$off, I32:$addr, F64:$val)>;
|
||||
(STORE_F64 texternalsym:$off, I32:$addr, 0, F64:$val)>;
|
||||
|
||||
// Select stores with just a constant offset.
|
||||
def : Pat<(store I32:$val, imm:$off),
|
||||
(STORE_I32 imm:$off, (CONST_I32 0), I32:$val)>;
|
||||
(STORE_I32 imm:$off, (CONST_I32 0), 0, I32:$val)>;
|
||||
def : Pat<(store I64:$val, imm:$off),
|
||||
(STORE_I64 imm:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE_I64 imm:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
def : Pat<(store F32:$val, imm:$off),
|
||||
(STORE_F32 imm:$off, (CONST_I32 0), F32:$val)>;
|
||||
(STORE_F32 imm:$off, (CONST_I32 0), 0, F32:$val)>;
|
||||
def : Pat<(store F64:$val, imm:$off),
|
||||
(STORE_F64 imm:$off, (CONST_I32 0), F64:$val)>;
|
||||
(STORE_F64 imm:$off, (CONST_I32 0), 0, F64:$val)>;
|
||||
def : Pat<(store I32:$val, (WebAssemblywrapper tglobaladdr:$off)),
|
||||
(STORE_I32 tglobaladdr:$off, (CONST_I32 0), I32:$val)>;
|
||||
(STORE_I32 tglobaladdr:$off, (CONST_I32 0), 0, I32:$val)>;
|
||||
def : Pat<(store I64:$val, (WebAssemblywrapper tglobaladdr:$off)),
|
||||
(STORE_I64 tglobaladdr:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE_I64 tglobaladdr:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
def : Pat<(store F32:$val, (WebAssemblywrapper tglobaladdr:$off)),
|
||||
(STORE_F32 tglobaladdr:$off, (CONST_I32 0), F32:$val)>;
|
||||
(STORE_F32 tglobaladdr:$off, (CONST_I32 0), 0, F32:$val)>;
|
||||
def : Pat<(store F64:$val, (WebAssemblywrapper tglobaladdr:$off)),
|
||||
(STORE_F64 tglobaladdr:$off, (CONST_I32 0), F64:$val)>;
|
||||
(STORE_F64 tglobaladdr:$off, (CONST_I32 0), 0, F64:$val)>;
|
||||
def : Pat<(store I32:$val, (WebAssemblywrapper texternalsym:$off)),
|
||||
(STORE_I32 texternalsym:$off, (CONST_I32 0), I32:$val)>;
|
||||
(STORE_I32 texternalsym:$off, (CONST_I32 0), 0, I32:$val)>;
|
||||
def : Pat<(store I64:$val, (WebAssemblywrapper texternalsym:$off)),
|
||||
(STORE_I64 texternalsym:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE_I64 texternalsym:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
def : Pat<(store F32:$val, (WebAssemblywrapper texternalsym:$off)),
|
||||
(STORE_F32 texternalsym:$off, (CONST_I32 0), F32:$val)>;
|
||||
(STORE_F32 texternalsym:$off, (CONST_I32 0), 0, F32:$val)>;
|
||||
def : Pat<(store F64:$val, (WebAssemblywrapper texternalsym:$off)),
|
||||
(STORE_F64 texternalsym:$off, (CONST_I32 0), F64:$val)>;
|
||||
(STORE_F64 texternalsym:$off, (CONST_I32 0), 0, F64:$val)>;
|
||||
|
||||
let Defs = [ARGUMENTS] in {
|
||||
|
||||
// Truncating store.
|
||||
def STORE8_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr, I32:$val), [],
|
||||
"i32.store8\t$dst, ${off}(${addr}), $val">;
|
||||
def STORE16_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr, I32:$val), [],
|
||||
"i32.store16\t$dst, ${off}(${addr}), $val">;
|
||||
def STORE8_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr, I64:$val), [],
|
||||
"i64.store8\t$dst, ${off}(${addr}), $val">;
|
||||
def STORE16_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr, I64:$val), [],
|
||||
"i64.store16\t$dst, ${off}(${addr}), $val">;
|
||||
def STORE32_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr, I64:$val), [],
|
||||
"i64.store32\t$dst, ${off}(${addr}), $val">;
|
||||
def STORE8_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align, I32:$val), [],
|
||||
"i32.store8\t$dst, ${off}(${addr})${p2align}, $val">;
|
||||
def STORE16_I32 : I<(outs I32:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align, I32:$val), [],
|
||||
"i32.store16\t$dst, ${off}(${addr})${p2align}, $val">;
|
||||
def STORE8_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align, I64:$val), [],
|
||||
"i64.store8\t$dst, ${off}(${addr})${p2align}, $val">;
|
||||
def STORE16_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align, I64:$val), [],
|
||||
"i64.store16\t$dst, ${off}(${addr})${p2align}, $val">;
|
||||
def STORE32_I64 : I<(outs I64:$dst), (ins i32imm:$off, I32:$addr,
|
||||
P2Align:$p2align, I64:$val), [],
|
||||
"i64.store32\t$dst, ${off}(${addr})${p2align}, $val">;
|
||||
|
||||
} // Defs = [ARGUMENTS]
|
||||
|
||||
// Select truncating stores with no constant offset.
|
||||
def : Pat<(truncstorei8 I32:$val, I32:$addr),
|
||||
(STORE8_I32 0, I32:$addr, I32:$val)>;
|
||||
(STORE8_I32 0, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(truncstorei16 I32:$val, I32:$addr),
|
||||
(STORE16_I32 0, I32:$addr, I32:$val)>;
|
||||
(STORE16_I32 0, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(truncstorei8 I64:$val, I32:$addr),
|
||||
(STORE8_I64 0, I32:$addr, I64:$val)>;
|
||||
(STORE8_I64 0, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(truncstorei16 I64:$val, I32:$addr),
|
||||
(STORE16_I64 0, I32:$addr, I64:$val)>;
|
||||
(STORE16_I64 0, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(truncstorei32 I64:$val, I32:$addr),
|
||||
(STORE32_I64 0, I32:$addr, I64:$val)>;
|
||||
(STORE32_I64 0, I32:$addr, 0, I64:$val)>;
|
||||
|
||||
// Select truncating stores with a constant offset.
|
||||
def : Pat<(truncstorei8 I32:$val, (regPlusImm I32:$addr, imm:$off)),
|
||||
(STORE8_I32 imm:$off, I32:$addr, I32:$val)>;
|
||||
(STORE8_I32 imm:$off, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(truncstorei16 I32:$val, (regPlusImm I32:$addr, imm:$off)),
|
||||
(STORE16_I32 imm:$off, I32:$addr, I32:$val)>;
|
||||
(STORE16_I32 imm:$off, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(truncstorei8 I64:$val, (regPlusImm I32:$addr, imm:$off)),
|
||||
(STORE8_I64 imm:$off, I32:$addr, I64:$val)>;
|
||||
(STORE8_I64 imm:$off, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(truncstorei16 I64:$val, (regPlusImm I32:$addr, imm:$off)),
|
||||
(STORE16_I64 imm:$off, I32:$addr, I64:$val)>;
|
||||
(STORE16_I64 imm:$off, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(truncstorei32 I64:$val, (regPlusImm I32:$addr, imm:$off)),
|
||||
(STORE32_I64 imm:$off, I32:$addr, I64:$val)>;
|
||||
(STORE32_I64 imm:$off, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(truncstorei8 I32:$val,
|
||||
(regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off))),
|
||||
(STORE8_I32 tglobaladdr:$off, I32:$addr, I32:$val)>;
|
||||
(STORE8_I32 tglobaladdr:$off, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(truncstorei16 I32:$val,
|
||||
(regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off))),
|
||||
(STORE16_I32 tglobaladdr:$off, I32:$addr, I32:$val)>;
|
||||
(STORE16_I32 tglobaladdr:$off, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(truncstorei8 I64:$val,
|
||||
(regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off))),
|
||||
(STORE8_I64 tglobaladdr:$off, I32:$addr, I64:$val)>;
|
||||
(STORE8_I64 tglobaladdr:$off, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(truncstorei16 I64:$val,
|
||||
(regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off))),
|
||||
(STORE16_I64 tglobaladdr:$off, I32:$addr, I64:$val)>;
|
||||
(STORE16_I64 tglobaladdr:$off, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(truncstorei32 I64:$val,
|
||||
(regPlusGA I32:$addr,
|
||||
(WebAssemblywrapper tglobaladdr:$off))),
|
||||
(STORE32_I64 tglobaladdr:$off, I32:$addr, I64:$val)>;
|
||||
(STORE32_I64 tglobaladdr:$off, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(truncstorei8 I32:$val, (add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off))),
|
||||
(STORE8_I32 texternalsym:$off, I32:$addr, I32:$val)>;
|
||||
(STORE8_I32 texternalsym:$off, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(truncstorei16 I32:$val,
|
||||
(add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off))),
|
||||
(STORE16_I32 texternalsym:$off, I32:$addr, I32:$val)>;
|
||||
(STORE16_I32 texternalsym:$off, I32:$addr, 0, I32:$val)>;
|
||||
def : Pat<(truncstorei8 I64:$val,
|
||||
(add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off))),
|
||||
(STORE8_I64 texternalsym:$off, I32:$addr, I64:$val)>;
|
||||
(STORE8_I64 texternalsym:$off, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(truncstorei16 I64:$val,
|
||||
(add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off))),
|
||||
(STORE16_I64 texternalsym:$off, I32:$addr, I64:$val)>;
|
||||
(STORE16_I64 texternalsym:$off, I32:$addr, 0, I64:$val)>;
|
||||
def : Pat<(truncstorei32 I64:$val,
|
||||
(add I32:$addr,
|
||||
(WebAssemblywrapper texternalsym:$off))),
|
||||
(STORE32_I64 texternalsym:$off, I32:$addr, I64:$val)>;
|
||||
(STORE32_I64 texternalsym:$off, I32:$addr, 0, I64:$val)>;
|
||||
|
||||
// Select truncating stores with just a constant offset.
|
||||
def : Pat<(truncstorei8 I32:$val, imm:$off),
|
||||
(STORE8_I32 imm:$off, (CONST_I32 0), I32:$val)>;
|
||||
(STORE8_I32 imm:$off, (CONST_I32 0), 0, I32:$val)>;
|
||||
def : Pat<(truncstorei16 I32:$val, imm:$off),
|
||||
(STORE16_I32 imm:$off, (CONST_I32 0), I32:$val)>;
|
||||
(STORE16_I32 imm:$off, (CONST_I32 0), 0, I32:$val)>;
|
||||
def : Pat<(truncstorei8 I64:$val, imm:$off),
|
||||
(STORE8_I64 imm:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE8_I64 imm:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
def : Pat<(truncstorei16 I64:$val, imm:$off),
|
||||
(STORE16_I64 imm:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE16_I64 imm:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
def : Pat<(truncstorei32 I64:$val, imm:$off),
|
||||
(STORE32_I64 imm:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE32_I64 imm:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
def : Pat<(truncstorei8 I32:$val, (WebAssemblywrapper tglobaladdr:$off)),
|
||||
(STORE8_I32 tglobaladdr:$off, (CONST_I32 0), I32:$val)>;
|
||||
(STORE8_I32 tglobaladdr:$off, (CONST_I32 0), 0, I32:$val)>;
|
||||
def : Pat<(truncstorei16 I32:$val, (WebAssemblywrapper tglobaladdr:$off)),
|
||||
(STORE16_I32 tglobaladdr:$off, (CONST_I32 0), I32:$val)>;
|
||||
(STORE16_I32 tglobaladdr:$off, (CONST_I32 0), 0, I32:$val)>;
|
||||
def : Pat<(truncstorei8 I64:$val, (WebAssemblywrapper tglobaladdr:$off)),
|
||||
(STORE8_I64 tglobaladdr:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE8_I64 tglobaladdr:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
def : Pat<(truncstorei16 I64:$val, (WebAssemblywrapper tglobaladdr:$off)),
|
||||
(STORE16_I64 tglobaladdr:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE16_I64 tglobaladdr:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
def : Pat<(truncstorei32 I64:$val, (WebAssemblywrapper tglobaladdr:$off)),
|
||||
(STORE32_I64 tglobaladdr:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE32_I64 tglobaladdr:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
def : Pat<(truncstorei8 I32:$val, (WebAssemblywrapper texternalsym:$off)),
|
||||
(STORE8_I32 texternalsym:$off, (CONST_I32 0), I32:$val)>;
|
||||
(STORE8_I32 texternalsym:$off, (CONST_I32 0), 0, I32:$val)>;
|
||||
def : Pat<(truncstorei16 I32:$val, (WebAssemblywrapper texternalsym:$off)),
|
||||
(STORE16_I32 texternalsym:$off, (CONST_I32 0), I32:$val)>;
|
||||
(STORE16_I32 texternalsym:$off, (CONST_I32 0), 0, I32:$val)>;
|
||||
def : Pat<(truncstorei8 I64:$val, (WebAssemblywrapper texternalsym:$off)),
|
||||
(STORE8_I64 texternalsym:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE8_I64 texternalsym:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
def : Pat<(truncstorei16 I64:$val, (WebAssemblywrapper texternalsym:$off)),
|
||||
(STORE16_I64 texternalsym:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE16_I64 texternalsym:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
def : Pat<(truncstorei32 I64:$val, (WebAssemblywrapper texternalsym:$off)),
|
||||
(STORE32_I64 texternalsym:$off, (CONST_I32 0), I64:$val)>;
|
||||
(STORE32_I64 texternalsym:$off, (CONST_I32 0), 0, I64:$val)>;
|
||||
|
||||
let Defs = [ARGUMENTS] in {
|
||||
|
||||
|
@ -58,6 +58,11 @@ public:
|
||||
VRegStackified.resize(TargetRegisterInfo::virtReg2Index(VReg) + 1);
|
||||
VRegStackified.set(TargetRegisterInfo::virtReg2Index(VReg));
|
||||
}
|
||||
void unstackifyVReg(unsigned VReg) {
|
||||
if (TargetRegisterInfo::virtReg2Index(VReg) >= VRegStackified.size())
|
||||
return;
|
||||
VRegStackified.reset(TargetRegisterInfo::virtReg2Index(VReg));
|
||||
}
|
||||
bool isVRegStackified(unsigned VReg) const {
|
||||
if (TargetRegisterInfo::virtReg2Index(VReg) >= VRegStackified.size())
|
||||
return false;
|
||||
|
@ -70,8 +70,10 @@ bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) {
|
||||
MachineOperand &MO = MI.getOperand(0);
|
||||
unsigned OldReg = MO.getReg();
|
||||
// TODO: Handle SP/physregs
|
||||
if (OldReg == MI.getOperand(3).getReg() &&
|
||||
TargetRegisterInfo::isVirtualRegister(MI.getOperand(3).getReg())) {
|
||||
if (OldReg ==
|
||||
MI.getOperand(WebAssembly::StoreValueOperandNo).getReg() &&
|
||||
TargetRegisterInfo::isVirtualRegister(
|
||||
MI.getOperand(WebAssembly::StoreValueOperandNo).getReg())) {
|
||||
Changed = true;
|
||||
unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(OldReg));
|
||||
MO.setReg(NewReg);
|
||||
|
108
lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp
Normal file
108
lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
//=- WebAssemblySetP2AlignOperands.cpp - Set alignments on loads and stores -=//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief This file sets the p2align operands on load and store instructions.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "WebAssembly.h"
|
||||
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
|
||||
#include "WebAssemblyMachineFunctionInfo.h"
|
||||
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
|
||||
#include "llvm/CodeGen/MachineMemOperand.h"
|
||||
#include "llvm/CodeGen/Passes.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "wasm-set-p2align-operands"
|
||||
|
||||
namespace {
|
||||
class WebAssemblySetP2AlignOperands final : public MachineFunctionPass {
|
||||
public:
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
WebAssemblySetP2AlignOperands() : MachineFunctionPass(ID) {}
|
||||
|
||||
const char *getPassName() const override {
|
||||
return "WebAssembly Set p2align Operands";
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.setPreservesCFG();
|
||||
AU.addPreserved<MachineBlockFrequencyInfo>();
|
||||
AU.addPreservedID(MachineDominatorsID);
|
||||
MachineFunctionPass::getAnalysisUsage(AU);
|
||||
}
|
||||
|
||||
bool runOnMachineFunction(MachineFunction &MF) override;
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
char WebAssemblySetP2AlignOperands::ID = 0;
|
||||
FunctionPass *llvm::createWebAssemblySetP2AlignOperands() {
|
||||
return new WebAssemblySetP2AlignOperands();
|
||||
}
|
||||
|
||||
bool WebAssemblySetP2AlignOperands::runOnMachineFunction(MachineFunction &MF) {
|
||||
DEBUG({
|
||||
dbgs() << "********** Set p2align Operands **********\n"
|
||||
<< "********** Function: " << MF.getName() << '\n';
|
||||
});
|
||||
|
||||
bool Changed = false;
|
||||
|
||||
for (auto &MBB : MF) {
|
||||
for (auto &MI : MBB) {
|
||||
switch (MI.getOpcode()) {
|
||||
case WebAssembly::LOAD_I32:
|
||||
case WebAssembly::LOAD_I64:
|
||||
case WebAssembly::LOAD_F32:
|
||||
case WebAssembly::LOAD_F64:
|
||||
case WebAssembly::LOAD8_S_I32:
|
||||
case WebAssembly::LOAD8_U_I32:
|
||||
case WebAssembly::LOAD16_S_I32:
|
||||
case WebAssembly::LOAD16_U_I32:
|
||||
case WebAssembly::LOAD8_S_I64:
|
||||
case WebAssembly::LOAD8_U_I64:
|
||||
case WebAssembly::LOAD16_S_I64:
|
||||
case WebAssembly::LOAD16_U_I64:
|
||||
case WebAssembly::LOAD32_S_I64:
|
||||
case WebAssembly::LOAD32_U_I64:
|
||||
case WebAssembly::STORE_I32:
|
||||
case WebAssembly::STORE_I64:
|
||||
case WebAssembly::STORE_F32:
|
||||
case WebAssembly::STORE_F64:
|
||||
case WebAssembly::STORE8_I32:
|
||||
case WebAssembly::STORE16_I32:
|
||||
case WebAssembly::STORE8_I64:
|
||||
case WebAssembly::STORE16_I64:
|
||||
case WebAssembly::STORE32_I64:
|
||||
assert(MI.getOperand(3).getImm() == 0 &&
|
||||
"ISel should set p2align operands to 0");
|
||||
assert(MI.hasOneMemOperand() &&
|
||||
"Load and store instructions have exactly one mem operand");
|
||||
assert((*MI.memoperands_begin())->getSize() ==
|
||||
(UINT64_C(1)
|
||||
<< WebAssembly::GetDefaultP2Align(MI.getOpcode())) &&
|
||||
"Default p2align value should be natural");
|
||||
assert(MI.getDesc().OpInfo[3].OperandType ==
|
||||
WebAssembly::OPERAND_P2ALIGN &&
|
||||
"Load and store instructions should have a p2align operand");
|
||||
MI.getOperand(3).setImm(
|
||||
Log2_64((*MI.memoperands_begin())->getAlignment()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Changed;
|
||||
}
|
@ -91,7 +91,8 @@ bool WebAssemblyStoreResults::runOnMachineFunction(MachineFunction &MF) {
|
||||
case WebAssembly::STORE_I32:
|
||||
case WebAssembly::STORE_I64:
|
||||
unsigned ToReg = MI.getOperand(0).getReg();
|
||||
unsigned FromReg = MI.getOperand(3).getReg();
|
||||
unsigned FromReg =
|
||||
MI.getOperand(WebAssembly::StoreValueOperandNo).getReg();
|
||||
for (auto I = MRI.use_begin(FromReg), E = MRI.use_end(); I != E;) {
|
||||
MachineOperand &O = *I++;
|
||||
MachineInstr *Where = O.getParent();
|
||||
|
@ -154,6 +154,10 @@ bool WebAssemblyPassConfig::addInstSelector() {
|
||||
// so that we can fix up the ARGUMENT instructions before anything else
|
||||
// sees them in the wrong place.
|
||||
addPass(createWebAssemblyArgumentMove());
|
||||
// Set the p2align operands. This information is present during ISel, however
|
||||
// it's inconvenient to collect. Collect it now, and update the immediate
|
||||
// operands.
|
||||
addPass(createWebAssemblySetP2AlignOperands());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
210
test/CodeGen/WebAssembly/i32-load-store-alignment.ll
Normal file
210
test/CodeGen/WebAssembly/i32-load-store-alignment.ll
Normal file
@ -0,0 +1,210 @@
|
||||
; RUN: llc < %s -asm-verbose=false | FileCheck %s
|
||||
|
||||
; Test loads and stores with custom alignment values.
|
||||
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32-unknown-unknown"
|
||||
|
||||
; CHECK-LABEL: ldi32_a1:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i32{{$}}
|
||||
; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i32 @ldi32_a1(i32 *%p) {
|
||||
%v = load i32, i32* %p, align 1
|
||||
ret i32 %v
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi32_a2:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i32{{$}}
|
||||
; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0):p2align=1{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i32 @ldi32_a2(i32 *%p) {
|
||||
%v = load i32, i32* %p, align 2
|
||||
ret i32 %v
|
||||
}
|
||||
|
||||
; 4 is the default alignment for i32 so no attribute is needed.
|
||||
|
||||
; CHECK-LABEL: ldi32_a4:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i32{{$}}
|
||||
; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i32 @ldi32_a4(i32 *%p) {
|
||||
%v = load i32, i32* %p, align 4
|
||||
ret i32 %v
|
||||
}
|
||||
|
||||
; The default alignment in LLVM is the same as the defualt alignment in wasm.
|
||||
|
||||
; CHECK-LABEL: ldi32:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i32{{$}}
|
||||
; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i32 @ldi32(i32 *%p) {
|
||||
%v = load i32, i32* %p
|
||||
ret i32 %v
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi32_a8:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i32{{$}}
|
||||
; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0):p2align=3{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i32 @ldi32_a8(i32 *%p) {
|
||||
%v = load i32, i32* %p, align 8
|
||||
ret i32 %v
|
||||
}
|
||||
|
||||
; Extending loads.
|
||||
|
||||
; CHECK-LABEL: ldi8_a1:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i32{{$}}
|
||||
; CHECK-NEXT: i32.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i8 @ldi8_a1(i8 *%p) {
|
||||
%v = load i8, i8* %p, align 1
|
||||
ret i8 %v
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi8_a2:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i32{{$}}
|
||||
; CHECK-NEXT: i32.load8_u $push[[NUM:[0-9]+]]=, 0($0):p2align=1{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i8 @ldi8_a2(i8 *%p) {
|
||||
%v = load i8, i8* %p, align 2
|
||||
ret i8 %v
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi16_a1:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i32{{$}}
|
||||
; CHECK-NEXT: i32.load16_u $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i16 @ldi16_a1(i16 *%p) {
|
||||
%v = load i16, i16* %p, align 1
|
||||
ret i16 %v
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi16_a2:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i32{{$}}
|
||||
; CHECK-NEXT: i32.load16_u $push[[NUM:[0-9]+]]=, 0($0){{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i16 @ldi16_a2(i16 *%p) {
|
||||
%v = load i16, i16* %p, align 2
|
||||
ret i16 %v
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi16_a4:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i32{{$}}
|
||||
; CHECK-NEXT: i32.load16_u $push[[NUM:[0-9]+]]=, 0($0):p2align=2{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i16 @ldi16_a4(i16 *%p) {
|
||||
%v = load i16, i16* %p, align 4
|
||||
ret i16 %v
|
||||
}
|
||||
|
||||
; Stores.
|
||||
|
||||
; CHECK-LABEL: sti32_a1:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK-NEXT: i32.store $discard=, 0($0):p2align=0, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti32_a1(i32 *%p, i32 %v) {
|
||||
store i32 %v, i32* %p, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti32_a2:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK-NEXT: i32.store $discard=, 0($0):p2align=1, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti32_a2(i32 *%p, i32 %v) {
|
||||
store i32 %v, i32* %p, align 2
|
||||
ret void
|
||||
}
|
||||
|
||||
; 4 is the default alignment for i32 so no attribute is needed.
|
||||
|
||||
; CHECK-LABEL: sti32_a4:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK-NEXT: i32.store $discard=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti32_a4(i32 *%p, i32 %v) {
|
||||
store i32 %v, i32* %p, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; The default alignment in LLVM is the same as the defualt alignment in wasm.
|
||||
|
||||
; CHECK-LABEL: sti32:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK-NEXT: i32.store $discard=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti32(i32 *%p, i32 %v) {
|
||||
store i32 %v, i32* %p
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti32_a8:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK-NEXT: i32.store $discard=, 0($0):p2align=3, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti32_a8(i32 *%p, i32 %v) {
|
||||
store i32 %v, i32* %p, align 8
|
||||
ret void
|
||||
}
|
||||
|
||||
; Truncating stores.
|
||||
|
||||
; CHECK-LABEL: sti8_a1:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK-NEXT: i32.store8 $discard=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti8_a1(i8 *%p, i8 %v) {
|
||||
store i8 %v, i8* %p, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti8_a2:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK-NEXT: i32.store8 $discard=, 0($0):p2align=1, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti8_a2(i8 *%p, i8 %v) {
|
||||
store i8 %v, i8* %p, align 2
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti16_a1:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK-NEXT: i32.store16 $discard=, 0($0):p2align=0, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti16_a1(i16 *%p, i16 %v) {
|
||||
store i16 %v, i16* %p, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti16_a2:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK-NEXT: i32.store16 $discard=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti16_a2(i16 *%p, i16 %v) {
|
||||
store i16 %v, i16* %p, align 2
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti16_a4:
|
||||
; CHECK-NEXT: .param i32, i32{{$}}
|
||||
; CHECK-NEXT: i32.store16 $discard=, 0($0):p2align=2, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti16_a4(i16 *%p, i16 %v) {
|
||||
store i16 %v, i16* %p, align 4
|
||||
ret void
|
||||
}
|
323
test/CodeGen/WebAssembly/i64-load-store-alignment.ll
Normal file
323
test/CodeGen/WebAssembly/i64-load-store-alignment.ll
Normal file
@ -0,0 +1,323 @@
|
||||
; RUN: llc < %s -asm-verbose=false | FileCheck %s
|
||||
|
||||
; Test loads and stores with custom alignment values.
|
||||
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32-unknown-unknown"
|
||||
|
||||
; CHECK-LABEL: ldi64_a1:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi64_a1(i64 *%p) {
|
||||
%v = load i64, i64* %p, align 1
|
||||
ret i64 %v
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi64_a2:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0):p2align=1{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi64_a2(i64 *%p) {
|
||||
%v = load i64, i64* %p, align 2
|
||||
ret i64 %v
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi64_a4:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0):p2align=2{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi64_a4(i64 *%p) {
|
||||
%v = load i64, i64* %p, align 4
|
||||
ret i64 %v
|
||||
}
|
||||
|
||||
; 8 is the default alignment for i32 so no attribute is needed.
|
||||
|
||||
; CHECK-LABEL: ldi64_a8:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi64_a8(i64 *%p) {
|
||||
%v = load i64, i64* %p, align 8
|
||||
ret i64 %v
|
||||
}
|
||||
|
||||
; The default alignment in LLVM is the same as the defualt alignment in wasm.
|
||||
|
||||
; CHECK-LABEL: ldi64:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0){{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi64(i64 *%p) {
|
||||
%v = load i64, i64* %p
|
||||
ret i64 %v
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi64_a16:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load $push[[NUM:[0-9]+]]=, 0($0):p2align=4{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi64_a16(i64 *%p) {
|
||||
%v = load i64, i64* %p, align 16
|
||||
ret i64 %v
|
||||
}
|
||||
|
||||
; Extending loads.
|
||||
|
||||
; CHECK-LABEL: ldi8_a1:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load8_u $push[[NUM:[0-9]+]]=, 0($0){{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi8_a1(i8 *%p) {
|
||||
%v = load i8, i8* %p, align 1
|
||||
%w = zext i8 %v to i64
|
||||
ret i64 %w
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi8_a2:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load8_u $push[[NUM:[0-9]+]]=, 0($0):p2align=1{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi8_a2(i8 *%p) {
|
||||
%v = load i8, i8* %p, align 2
|
||||
%w = zext i8 %v to i64
|
||||
ret i64 %w
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi16_a1:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load16_u $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi16_a1(i16 *%p) {
|
||||
%v = load i16, i16* %p, align 1
|
||||
%w = zext i16 %v to i64
|
||||
ret i64 %w
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi16_a2:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load16_u $push[[NUM:[0-9]+]]=, 0($0){{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi16_a2(i16 *%p) {
|
||||
%v = load i16, i16* %p, align 2
|
||||
%w = zext i16 %v to i64
|
||||
ret i64 %w
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi16_a4:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load16_u $push[[NUM:[0-9]+]]=, 0($0):p2align=2{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi16_a4(i16 *%p) {
|
||||
%v = load i16, i16* %p, align 4
|
||||
%w = zext i16 %v to i64
|
||||
ret i64 %w
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi32_a1:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0):p2align=0{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi32_a1(i32 *%p) {
|
||||
%v = load i32, i32* %p, align 1
|
||||
%w = zext i32 %v to i64
|
||||
ret i64 %w
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi32_a2:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0):p2align=1{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi32_a2(i32 *%p) {
|
||||
%v = load i32, i32* %p, align 2
|
||||
%w = zext i32 %v to i64
|
||||
ret i64 %w
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi32_a4:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0){{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi32_a4(i32 *%p) {
|
||||
%v = load i32, i32* %p, align 4
|
||||
%w = zext i32 %v to i64
|
||||
ret i64 %w
|
||||
}
|
||||
|
||||
; CHECK-LABEL: ldi32_a8:
|
||||
; CHECK-NEXT: .param i32{{$}}
|
||||
; CHECK-NEXT: .result i64{{$}}
|
||||
; CHECK-NEXT: i64.load32_u $push[[NUM:[0-9]+]]=, 0($0):p2align=3{{$}}
|
||||
; CHECK-NEXT: return $pop[[NUM]]{{$}}
|
||||
define i64 @ldi32_a8(i32 *%p) {
|
||||
%v = load i32, i32* %p, align 8
|
||||
%w = zext i32 %v to i64
|
||||
ret i64 %w
|
||||
}
|
||||
|
||||
; Stores.
|
||||
|
||||
; CHECK-LABEL: sti64_a1:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store $discard=, 0($0):p2align=0, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti64_a1(i64 *%p, i64 %v) {
|
||||
store i64 %v, i64* %p, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti64_a2:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store $discard=, 0($0):p2align=1, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti64_a2(i64 *%p, i64 %v) {
|
||||
store i64 %v, i64* %p, align 2
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti64_a4:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store $discard=, 0($0):p2align=2, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti64_a4(i64 *%p, i64 %v) {
|
||||
store i64 %v, i64* %p, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; 8 is the default alignment for i32 so no attribute is needed.
|
||||
|
||||
; CHECK-LABEL: sti64_a8:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store $discard=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti64_a8(i64 *%p, i64 %v) {
|
||||
store i64 %v, i64* %p, align 8
|
||||
ret void
|
||||
}
|
||||
|
||||
; The default alignment in LLVM is the same as the defualt alignment in wasm.
|
||||
|
||||
; CHECK-LABEL: sti64:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store $discard=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti64(i64 *%p, i64 %v) {
|
||||
store i64 %v, i64* %p
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti64_a16:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store $discard=, 0($0):p2align=4, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti64_a16(i64 *%p, i64 %v) {
|
||||
store i64 %v, i64* %p, align 16
|
||||
ret void
|
||||
}
|
||||
|
||||
; Truncating stores.
|
||||
|
||||
; CHECK-LABEL: sti8_a1:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store8 $discard=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti8_a1(i8 *%p, i64 %w) {
|
||||
%v = trunc i64 %w to i8
|
||||
store i8 %v, i8* %p, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti8_a2:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store8 $discard=, 0($0):p2align=1, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti8_a2(i8 *%p, i64 %w) {
|
||||
%v = trunc i64 %w to i8
|
||||
store i8 %v, i8* %p, align 2
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti16_a1:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store16 $discard=, 0($0):p2align=0, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti16_a1(i16 *%p, i64 %w) {
|
||||
%v = trunc i64 %w to i16
|
||||
store i16 %v, i16* %p, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti16_a2:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store16 $discard=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti16_a2(i16 *%p, i64 %w) {
|
||||
%v = trunc i64 %w to i16
|
||||
store i16 %v, i16* %p, align 2
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti16_a4:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store16 $discard=, 0($0):p2align=2, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti16_a4(i16 *%p, i64 %w) {
|
||||
%v = trunc i64 %w to i16
|
||||
store i16 %v, i16* %p, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti32_a1:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store32 $discard=, 0($0):p2align=0, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti32_a1(i32 *%p, i64 %w) {
|
||||
%v = trunc i64 %w to i32
|
||||
store i32 %v, i32* %p, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti32_a2:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store32 $discard=, 0($0):p2align=1, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti32_a2(i32 *%p, i64 %w) {
|
||||
%v = trunc i64 %w to i32
|
||||
store i32 %v, i32* %p, align 2
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti32_a4:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store32 $discard=, 0($0), $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti32_a4(i32 *%p, i64 %w) {
|
||||
%v = trunc i64 %w to i32
|
||||
store i32 %v, i32* %p, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: sti32_a8:
|
||||
; CHECK-NEXT: .param i32, i64{{$}}
|
||||
; CHECK-NEXT: i64.store32 $discard=, 0($0):p2align=3, $1{{$}}
|
||||
; CHECK-NEXT: return{{$}}
|
||||
define void @sti32_a8(i32 *%p, i64 %w) {
|
||||
%v = trunc i64 %w to i32
|
||||
store i32 %v, i32* %p, align 8
|
||||
ret void
|
||||
}
|
@ -372,14 +372,27 @@ define void @aggregate_load_store({i32,i32,i32,i32}* %p, {i32,i32,i32,i32}* %q)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Fold the offsets when lowering aggregate return values.
|
||||
; Fold the offsets when lowering aggregate return values. The stores get
|
||||
; merged into i64 stores.
|
||||
|
||||
; CHECK-LABEL: aggregate_return:
|
||||
; CHECK: i32.const $push0=, 0{{$}}
|
||||
; CHECK: i32.store $push1=, 12($0), $pop0{{$}}
|
||||
; CHECK: i32.store $push2=, 8($0), $pop1{{$}}
|
||||
; CHECK: i32.store $push3=, 4($0), $pop2{{$}}
|
||||
; CHECK: i32.store $discard=, 0($0), $pop3{{$}}
|
||||
; CHECK: i64.const $push0=, 0{{$}}
|
||||
; CHECK: i64.store $push1=, 8($0):p2align=2, $pop0{{$}}
|
||||
; CHECK: i64.store $discard=, 0($0):p2align=2, $pop1{{$}}
|
||||
define {i32,i32,i32,i32} @aggregate_return() {
|
||||
ret {i32,i32,i32,i32} zeroinitializer
|
||||
}
|
||||
|
||||
; Fold the offsets when lowering aggregate return values. The stores are not
|
||||
; merged.
|
||||
|
||||
; CHECK-LABEL: aggregate_return_without_merge:
|
||||
; CHECK: i32.const $push0=, 0{{$}}
|
||||
; CHECK: i32.store8 $push1=, 14($0), $pop0{{$}}
|
||||
; CHECK: i32.store16 $push2=, 12($0), $pop1{{$}}
|
||||
; CHECK: i32.store $discard=, 8($0), $pop2{{$}}
|
||||
; CHECK: i64.const $push3=, 0{{$}}
|
||||
; CHECK: i64.store $discard=, 0($0), $pop3{{$}}
|
||||
define {i64,i32,i16,i8} @aggregate_return_without_merge() {
|
||||
ret {i64,i32,i16,i8} zeroinitializer
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ define void @allocarray() {
|
||||
; CHECK-NEXT: i32.store [[SP]]=, 0([[L2]]), [[SP]]
|
||||
%r = alloca [5 x i32]
|
||||
|
||||
; CHECK-NEXT: i32.const $push[[L4:.+]]=, 4
|
||||
; CHECK-NEXT: i32.const $push[[L4:.+]]=, 12
|
||||
; CHECK-NEXT: i32.const [[L5:.+]]=, 12
|
||||
; CHECK-NEXT: i32.add [[L5]]=, [[SP]], [[L5]]
|
||||
; CHECK-NEXT: i32.add $push[[L6:.+]]=, [[L5]], $pop[[L4]]
|
||||
@ -66,7 +66,7 @@ define void @allocarray() {
|
||||
; CHECK-NEXT: i32.store $discard=, 0($pop3), $pop[[L10]]{{$}}
|
||||
%p = getelementptr [5 x i32], [5 x i32]* %r, i32 0, i32 0
|
||||
store i32 1, i32* %p
|
||||
%p2 = getelementptr [5 x i32], [5 x i32]* %r, i32 0, i32 1
|
||||
%p2 = getelementptr [5 x i32], [5 x i32]* %r, i32 0, i32 3
|
||||
store i32 1, i32* %p2
|
||||
|
||||
; CHECK-NEXT: i32.const [[L7:.+]]=, 32
|
||||
@ -89,8 +89,8 @@ define void @allocarray_inbounds() {
|
||||
%p = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 0
|
||||
store i32 1, i32* %p
|
||||
; This store should have both the GEP and the FI folded into it.
|
||||
; CHECK-NEXT: i32.store {{.*}}=, 16([[SP]]), $pop
|
||||
%p2 = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 1
|
||||
; CHECK-NEXT: i32.store {{.*}}=, 24([[SP]]), $pop
|
||||
%p2 = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 3
|
||||
store i32 1, i32* %p2
|
||||
; CHECK: i32.const [[L7:.+]]=, 32
|
||||
; CHECK-NEXT: i32.add [[SP]]=, [[SP]], [[L7]]
|
||||
|
Loading…
Reference in New Issue
Block a user