522 changes: 280 additions & 242 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo {
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;
Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
108 changes: 108 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 2 additions & 1 deletion llvm/lib/Target/WebAssembly/WebAssemblyStoreResults.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
210 changes: 210 additions & 0 deletions llvm/test/CodeGen/WebAssembly/i32-load-store-alignment.ll
Original file line number Diff line number Diff line change
@@ -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 changes: 323 additions & 0 deletions llvm/test/CodeGen/WebAssembly/i64-load-store-alignment.ll
Original file line number Diff line number Diff line change
@@ -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
}
25 changes: 19 additions & 6 deletions llvm/test/CodeGen/WebAssembly/offset.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
8 changes: 4 additions & 4 deletions llvm/test/CodeGen/WebAssembly/userstack.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand All @@ -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
Expand All @@ -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]]
Expand Down