Skip to content

Commit

Permalink
[WebAssembly] Implement instruction selection for constant offsets in…
Browse files Browse the repository at this point in the history
… addresses.

Add instruction patterns for matching load and store instructions with constant
offsets in addresses. The code is fairly redundant due to the need to replicate
everything between imm, tglobaldadr, and texternalsym, but this appears to be
common tablegen practice. The main alternative appears to be to introduce
matching functions with C++ code, but sticking with purely generated matchers
seems better for now.

Also note that this doesn't yet support offsets from getelementptr, which will
be the most common case; that will depend on a change in target-independent code
in order to set the NoUnsignedWrap flag, which I'll submit separately. Until
then, the testcase uses ptrtoint+add+inttoptr with a nuw on the add.

Also implement isLegalAddressingMode with an approximation of this.

Differential Revision: http://reviews.llvm.org/D15538

llvm-svn: 255681
  • Loading branch information
Dan Gohman committed Dec 15, 2015
1 parent 38b9a32 commit 4b9d791
Show file tree
Hide file tree
Showing 7 changed files with 574 additions and 28 deletions.
5 changes: 0 additions & 5 deletions llvm/lib/Target/WebAssembly/README.txt
Expand Up @@ -38,11 +38,6 @@ many set_local instructions are implicit!

//===---------------------------------------------------------------------===//

Load and store instructions can have a constant offset. We should (a) model
this, and (b) do address-mode folding with it.

//===---------------------------------------------------------------------===//

Br, br_if, and tableswitch instructions can support having a value on the
expression stack across the jump (sometimes). We should (a) model this, and
(b) extend the stackifier to utilize it.
Expand Down
27 changes: 23 additions & 4 deletions llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
Expand Up @@ -261,6 +261,24 @@ bool WebAssemblyTargetLowering::isCheapToSpeculateCtlz() const {
return true;
}

bool WebAssemblyTargetLowering::isLegalAddressingMode(const DataLayout &DL,
const AddrMode &AM,
Type *Ty,
unsigned AS) const {
// WebAssembly offsets are added as unsigned without wrapping. The
// isLegalAddressingMode gives us no way to determine if wrapping could be
// happening, so we approximate this by accepting only non-negative offsets.
if (AM.BaseOffs < 0)
return false;

// WebAssembly has no scale register operands.
if (AM.Scale != 0)
return false;

// Everything else is legal.
return true;
}

//===----------------------------------------------------------------------===//
// WebAssembly Lowering private implementation.
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -408,7 +426,8 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
if (In.Flags.isInConsecutiveRegs())
fail(DL, DAG, "WebAssembly hasn't implemented cons regs return values");
if (In.Flags.isInConsecutiveRegsLast())
fail(DL, DAG, "WebAssembly hasn't implemented cons regs last return values");
fail(DL, DAG,
"WebAssembly hasn't implemented cons regs last return values");
// Ignore In.getOrigAlign() because all our arguments are passed in
// registers.
Tys.push_back(In.VT);
Expand Down Expand Up @@ -551,9 +570,9 @@ SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op,
assert(GA->getTargetFlags() == 0 && "WebAssembly doesn't set target flags");
if (GA->getAddressSpace() != 0)
fail(DL, DAG, "WebAssembly only expects the 0 address space");
return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT,
GA->getOffset()));
return DAG.getNode(
WebAssemblyISD::Wrapper, DL, VT,
DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset()));
}

SDValue
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
Expand Up @@ -54,6 +54,8 @@ class WebAssemblyTargetLowering final : public TargetLowering {
StringRef Constraint, MVT VT) const override;
bool isCheapToSpeculateCttz() const override;
bool isCheapToSpeculateCtlz() const override;
bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty,
unsigned AS) const override;

SDValue LowerCall(CallLoweringInfo &CLI,
SmallVectorImpl<SDValue> &InVals) const override;
Expand Down
375 changes: 360 additions & 15 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions llvm/test/CodeGen/WebAssembly/global.ll
Expand Up @@ -10,8 +10,8 @@ target triple = "wasm32-unknown-unknown"
@llvm.used = appending global [1 x i32*] [i32* @g], section "llvm.metadata"

; CHECK: foo:
; CHECK: i32.const $push0=, answer{{$}}
; CHECK-NEXT: i32.load $push1=, 0($pop0){{$}}
; CHECK: i32.const $push0=, 0{{$}}
; CHECK-NEXT: i32.load $push1=, answer($pop0){{$}}
; CHECK-NEXT: return $pop1{{$}}
define i32 @foo() {
%a = load i32, i32* @answer
Expand Down
185 changes: 185 additions & 0 deletions llvm/test/CodeGen/WebAssembly/offset.ll
@@ -0,0 +1,185 @@
; RUN: llc < %s -asm-verbose=false | FileCheck %s

; Test constant load and store address offsets.

target datalayout = "e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"

; With an nuw add, we can fold an offset.

; CHECK-LABEL: load_i32_with_folded_offset:
; CHECK: i32.load $push0=, 24($0){{$}}
define i32 @load_i32_with_folded_offset(i32* %p) {
%q = ptrtoint i32* %p to i32
%r = add nuw i32 %q, 24
%s = inttoptr i32 %r to i32*
%t = load i32, i32* %s
ret i32 %t
}

; Without nuw, and even with nsw, we can't fold an offset.

; CHECK-LABEL: load_i32_with_unfolded_offset:
; CHECK: i32.const $push0=, 24{{$}}
; CHECK: i32.add $push1=, $0, $pop0{{$}}
; CHECK: i32.load $push2=, 0($pop1){{$}}
define i32 @load_i32_with_unfolded_offset(i32* %p) {
%q = ptrtoint i32* %p to i32
%r = add nsw i32 %q, 24
%s = inttoptr i32 %r to i32*
%t = load i32, i32* %s
ret i32 %t
}

; Same as above but with i64.

; CHECK-LABEL: load_i64_with_folded_offset:
; CHECK: i64.load $push0=, 24($0){{$}}
define i64 @load_i64_with_folded_offset(i64* %p) {
%q = ptrtoint i64* %p to i32
%r = add nuw i32 %q, 24
%s = inttoptr i32 %r to i64*
%t = load i64, i64* %s
ret i64 %t
}

; Same as above but with i64.

; CHECK-LABEL: load_i64_with_unfolded_offset:
; CHECK: i32.const $push0=, 24{{$}}
; CHECK: i32.add $push1=, $0, $pop0{{$}}
; CHECK: i64.load $push2=, 0($pop1){{$}}
define i64 @load_i64_with_unfolded_offset(i64* %p) {
%q = ptrtoint i64* %p to i32
%r = add nsw i32 %q, 24
%s = inttoptr i32 %r to i64*
%t = load i64, i64* %s
ret i64 %t
}

; Same as above but with store.

; CHECK-LABEL: store_i32_with_folded_offset:
; CHECK: i32.store $discard=, 24($0), $pop0{{$}}
define void @store_i32_with_folded_offset(i32* %p) {
%q = ptrtoint i32* %p to i32
%r = add nuw i32 %q, 24
%s = inttoptr i32 %r to i32*
store i32 0, i32* %s
ret void
}

; Same as above but with store.

; CHECK-LABEL: store_i32_with_unfolded_offset:
; CHECK: i32.const $push0=, 24{{$}}
; CHECK: i32.add $push1=, $0, $pop0{{$}}
; CHECK: i32.store $discard=, 0($pop1), $pop2{{$}}
define void @store_i32_with_unfolded_offset(i32* %p) {
%q = ptrtoint i32* %p to i32
%r = add nsw i32 %q, 24
%s = inttoptr i32 %r to i32*
store i32 0, i32* %s
ret void
}

; Same as above but with store with i64.

; CHECK-LABEL: store_i64_with_folded_offset:
; CHECK: i64.store $discard=, 24($0), $pop0{{$}}
define void @store_i64_with_folded_offset(i64* %p) {
%q = ptrtoint i64* %p to i32
%r = add nuw i32 %q, 24
%s = inttoptr i32 %r to i64*
store i64 0, i64* %s
ret void
}

; Same as above but with store with i64.

; CHECK-LABEL: store_i64_with_unfolded_offset:
; CHECK: i32.const $push0=, 24{{$}}
; CHECK: i32.add $push1=, $0, $pop0{{$}}
; CHECK: i64.store $discard=, 0($pop1), $pop2{{$}}
define void @store_i64_with_unfolded_offset(i64* %p) {
%q = ptrtoint i64* %p to i32
%r = add nsw i32 %q, 24
%s = inttoptr i32 %r to i64*
store i64 0, i64* %s
ret void
}

; When loading from a fixed address, materialize a zero.

; CHECK-LABEL: load_i32_from_numeric_address
; CHECK: i32.const $push0=, 0{{$}}
; CHECK: i32.load $push1=, 42($pop0){{$}}
define i32 @load_i32_from_numeric_address() {
%s = inttoptr i32 42 to i32*
%t = load i32, i32* %s
ret i32 %t
}

; CHECK-LABEL: load_i32_from_global_address
; CHECK: i32.const $push0=, 0{{$}}
; CHECK: i32.load $push1=, gv($pop0){{$}}
@gv = global i32 0
define i32 @load_i32_from_global_address() {
%t = load i32, i32* @gv
ret i32 %t
}

; CHECK-LABEL: store_i32_to_numeric_address:
; CHECK: i32.const $0=, 0{{$}}
; CHECK: i32.store $discard=, 42($0), $0{{$}}
define void @store_i32_to_numeric_address() {
%s = inttoptr i32 42 to i32*
store i32 0, i32* %s
ret void
}

; CHECK-LABEL: store_i32_to_global_address:
; CHECK: i32.const $0=, 0{{$}}
; CHECK: i32.store $discard=, gv($0), $0{{$}}
define void @store_i32_to_global_address() {
store i32 0, i32* @gv
ret void
}

; Fold an offset into a sign-extending load.

; CHECK-LABEL: load_i8_s_with_folded_offset:
; CHECK: i32.load8_s $push0=, 24($0){{$}}
define i32 @load_i8_s_with_folded_offset(i8* %p) {
%q = ptrtoint i8* %p to i32
%r = add nuw i32 %q, 24
%s = inttoptr i32 %r to i8*
%t = load i8, i8* %s
%u = sext i8 %t to i32
ret i32 %u
}

; Fold an offset into a zero-extending load.

; CHECK-LABEL: load_i8_u_with_folded_offset:
; CHECK: i32.load8_u $push0=, 24($0){{$}}
define i32 @load_i8_u_with_folded_offset(i8* %p) {
%q = ptrtoint i8* %p to i32
%r = add nuw i32 %q, 24
%s = inttoptr i32 %r to i8*
%t = load i8, i8* %s
%u = zext i8 %t to i32
ret i32 %u
}

; Fold an offset into a truncating store.

; CHECK-LABEL: store_i8_with_folded_offset:
; CHECK: i32.store8 $discard=, 24($0), $pop0{{$}}
define void @store_i8_with_folded_offset(i8* %p) {
%q = ptrtoint i8* %p to i32
%r = add nuw i32 %q, 24
%s = inttoptr i32 %r to i8*
store i8 0, i8* %s
ret void
}
4 changes: 2 additions & 2 deletions llvm/test/CodeGen/WebAssembly/store-results.ll
Expand Up @@ -26,7 +26,7 @@ entry:
@pos = global %class.Vec3 zeroinitializer, align 4

; CHECK-LABEL: foo:
; CHECK: i32.store $discard=, 0($pop0), $0{{$}}
; CHECK: i32.store $discard=, pos($0), $0{{$}}
define void @foo() {
for.body.i:
br label %for.body5.i
Expand All @@ -44,7 +44,7 @@ for.cond.cleanup4.i:
}

; CHECK-LABEL: bar:
; CHECK: i32.store $discard=, 0($0), $pop0{{$}}
; CHECK: i32.store $discard=, pos($0), $0{{$}}
define void @bar() {
for.body.i:
br label %for.body5.i
Expand Down

0 comments on commit 4b9d791

Please sign in to comment.