Skip to content

Commit 06c51ab

Browse files
Fix lowering of loads (and extending loads) from addrspace(1) globals
1 parent 26a9339 commit 06c51ab

File tree

2 files changed

+300
-4
lines changed

2 files changed

+300
-4
lines changed

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "WebAssemblyTargetMachine.h"
2020
#include "WebAssemblyUtilities.h"
2121
#include "llvm/CodeGen/CallingConvLower.h"
22+
#include "llvm/CodeGen/ISDOpcodes.h"
2223
#include "llvm/CodeGen/MachineFrameInfo.h"
2324
#include "llvm/CodeGen/MachineInstrBuilder.h"
2425
#include "llvm/CodeGen/MachineJumpTableInfo.h"
@@ -111,6 +112,17 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
111112
}
112113
}
113114

115+
// Likewise, transform extending loads for address space 1
116+
for (auto T : {MVT::i32, MVT::i64}) {
117+
for (auto S : {MVT::i8, MVT::i16, MVT::i32}) {
118+
if (T != S) {
119+
setLoadExtAction(ISD::EXTLOAD, T, S, Custom);
120+
setLoadExtAction(ISD::ZEXTLOAD, T, S, Custom);
121+
setLoadExtAction(ISD::SEXTLOAD, T, S, Custom);
122+
}
123+
}
124+
}
125+
114126
setOperationAction(ISD::GlobalAddress, MVTPtr, Custom);
115127
setOperationAction(ISD::GlobalTLSAddress, MVTPtr, Custom);
116128
setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom);
@@ -1707,6 +1719,11 @@ static bool IsWebAssemblyGlobal(SDValue Op) {
17071719
if (const GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Op))
17081720
return WebAssembly::isWasmVarAddressSpace(GA->getAddressSpace());
17091721

1722+
if (Op->getOpcode() == WebAssemblyISD::Wrapper)
1723+
if (const GlobalAddressSDNode *GA =
1724+
dyn_cast<GlobalAddressSDNode>(Op->getOperand(0)))
1725+
return WebAssembly::isWasmVarAddressSpace(GA->getAddressSpace());
1726+
17101727
return false;
17111728
}
17121729

@@ -1764,16 +1781,110 @@ SDValue WebAssemblyTargetLowering::LowerLoad(SDValue Op,
17641781
LoadSDNode *LN = cast<LoadSDNode>(Op.getNode());
17651782
const SDValue &Base = LN->getBasePtr();
17661783
const SDValue &Offset = LN->getOffset();
1784+
ISD::LoadExtType ExtType = LN->getExtensionType();
1785+
EVT ResultType = LN->getValueType(0);
17671786

17681787
if (IsWebAssemblyGlobal(Base)) {
17691788
if (!Offset->isUndef())
17701789
report_fatal_error(
17711790
"unexpected offset when loading from webassembly global", false);
17721791

1773-
SDVTList Tys = DAG.getVTList(LN->getValueType(0), MVT::Other);
1774-
SDValue Ops[] = {LN->getChain(), Base};
1775-
return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_GET, DL, Tys, Ops,
1776-
LN->getMemoryVT(), LN->getMemOperand());
1792+
EVT GT = MVT::INVALID_SIMPLE_VALUE_TYPE;
1793+
1794+
if (const GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Base))
1795+
GT = EVT::getEVT(GA->getGlobal()->getValueType());
1796+
if (Base->getOpcode() == WebAssemblyISD::Wrapper)
1797+
if (const GlobalAddressSDNode *GA =
1798+
dyn_cast<GlobalAddressSDNode>(Base->getOperand(0)))
1799+
GT = EVT::getEVT(GA->getGlobal()->getValueType());
1800+
1801+
if (GT != MVT::i8 && GT != MVT::i16 && GT != MVT::i32 && GT != MVT::i64 &&
1802+
GT != MVT::f32 && GT != MVT::f64)
1803+
report_fatal_error("encountered unexpected global type for Base when "
1804+
"loading from webassembly global",
1805+
false);
1806+
1807+
EVT PromotedGT = (GT == MVT::i8 || GT == MVT::i16) ? MVT::i32 : GT;
1808+
1809+
if (ExtType == ISD::NON_EXTLOAD) {
1810+
// A normal, non-extending load may try to load more or less than the
1811+
// underlying global, which is invalid. We lower this to a load of the
1812+
// global (i32 or i64) then truncate or extend as needed
1813+
1814+
// Modify the MMO to load the full global
1815+
MachineMemOperand *OldMMO = LN->getMemOperand();
1816+
MachineMemOperand *NewMMO = DAG.getMachineFunction().getMachineMemOperand(
1817+
OldMMO->getPointerInfo(), OldMMO->getFlags(),
1818+
LLT(PromotedGT.getSimpleVT()), OldMMO->getBaseAlign(),
1819+
OldMMO->getAAInfo(), OldMMO->getRanges(), OldMMO->getSyncScopeID(),
1820+
OldMMO->getSuccessOrdering(), OldMMO->getFailureOrdering());
1821+
1822+
SDVTList Tys = DAG.getVTList(PromotedGT, MVT::Other);
1823+
SDValue Ops[] = {LN->getChain(), Base};
1824+
SDValue GlobalGetNode = DAG.getMemIntrinsicNode(
1825+
WebAssemblyISD::GLOBAL_GET, DL, Tys, Ops, PromotedGT, NewMMO);
1826+
1827+
if (ResultType.bitsEq(PromotedGT)) {
1828+
return GlobalGetNode;
1829+
}
1830+
1831+
SDValue ValRes;
1832+
if (ResultType.isFloatingPoint())
1833+
ValRes = DAG.getFPExtendOrRound(GlobalGetNode, DL, ResultType);
1834+
else
1835+
ValRes = DAG.getAnyExtOrTrunc(GlobalGetNode, DL, ResultType);
1836+
1837+
return DAG.getMergeValues({ValRes, LN->getChain()}, DL);
1838+
}
1839+
1840+
if (ExtType == ISD::ZEXTLOAD || ExtType == ISD::SEXTLOAD) {
1841+
// Turn the unsupported load into an EXTLOAD followed by an
1842+
// explicit zero/sign extend inreg. Same as Expand
1843+
1844+
SDValue Result =
1845+
DAG.getExtLoad(ISD::EXTLOAD, DL, ResultType, LN->getChain(), Base,
1846+
LN->getMemoryVT(), LN->getMemOperand());
1847+
SDValue ValRes;
1848+
if (ExtType == ISD::SEXTLOAD)
1849+
ValRes = DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, Result.getValueType(),
1850+
Result, DAG.getValueType(LN->getMemoryVT()));
1851+
else
1852+
ValRes = DAG.getZeroExtendInReg(Result, DL, LN->getMemoryVT());
1853+
1854+
return DAG.getMergeValues({ValRes, LN->getChain()}, DL);
1855+
}
1856+
1857+
if (ExtType == ISD::EXTLOAD) {
1858+
// Expand the EXTLOAD into a regular LOAD of the global, and if
1859+
// needed, a zero-extension
1860+
1861+
EVT OldLoadType = LN->getMemoryVT();
1862+
EVT NewLoadType = (OldLoadType == MVT::i8 || OldLoadType == MVT::i16)
1863+
? MVT::i32
1864+
: OldLoadType;
1865+
1866+
// Modify the MMO to load a whole WASM "register"'s worth
1867+
MachineMemOperand *OldMMO = LN->getMemOperand();
1868+
MachineMemOperand *NewMMO = DAG.getMachineFunction().getMachineMemOperand(
1869+
OldMMO->getPointerInfo(), OldMMO->getFlags(),
1870+
LLT(NewLoadType.getSimpleVT()), OldMMO->getBaseAlign(),
1871+
OldMMO->getAAInfo(), OldMMO->getRanges(), OldMMO->getSyncScopeID(),
1872+
OldMMO->getSuccessOrdering(), OldMMO->getFailureOrdering());
1873+
1874+
SDValue Result =
1875+
DAG.getLoad(NewLoadType, DL, LN->getChain(), Base, NewMMO);
1876+
1877+
if (NewLoadType != ResultType) {
1878+
SDValue ValRes = DAG.getNode(ISD::ANY_EXTEND, DL, ResultType, Result);
1879+
return DAG.getMergeValues({ValRes, LN->getChain()}, DL);
1880+
}
1881+
1882+
return Result;
1883+
}
1884+
1885+
report_fatal_error(
1886+
"encountered unexpected ExtType when loading from webassembly global",
1887+
false);
17771888
}
17781889

17791890
if (std::optional<unsigned> Local = IsWebAssemblyLocal(Base, DAG)) {
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
2+
; RUN: llc < %s | FileCheck %s
3+
4+
; Test various loads from WASM (address space 1) globals lower as intended
5+
6+
target triple = "wasm32-unknown-unknown"
7+
8+
9+
@globalI8 = local_unnamed_addr addrspace(1) global i8 undef
10+
@globalI32 = local_unnamed_addr addrspace(1) global i32 undef
11+
@globalI64 = local_unnamed_addr addrspace(1) global i64 undef
12+
13+
14+
define i32 @zext_i8_i32() {
15+
; CHECK-LABEL: zext_i8_i32:
16+
; CHECK: .functype zext_i8_i32 () -> (i32)
17+
; CHECK-NEXT: # %bb.0:
18+
; CHECK-NEXT: global.get globalI32
19+
; CHECK-NEXT: i32.const 255
20+
; CHECK-NEXT: i32.and
21+
; CHECK-NEXT: # fallthrough-return
22+
%v = load i8, ptr addrspace(1) @globalI32
23+
%e = zext i8 %v to i32
24+
ret i32 %e
25+
}
26+
27+
define i32 @sext_i8_i32() {
28+
; CHECK-LABEL: sext_i8_i32:
29+
; CHECK: .functype sext_i8_i32 () -> (i32)
30+
; CHECK-NEXT: # %bb.0:
31+
; CHECK-NEXT: global.get globalI32
32+
; CHECK-NEXT: i32.extend8_s
33+
; CHECK-NEXT: # fallthrough-return
34+
%v = load i8, ptr addrspace(1) @globalI32
35+
%e = sext i8 %v to i32
36+
ret i32 %e
37+
}
38+
39+
define i32 @zext_i16_i32() {
40+
; CHECK-LABEL: zext_i16_i32:
41+
; CHECK: .functype zext_i16_i32 () -> (i32)
42+
; CHECK-NEXT: # %bb.0:
43+
; CHECK-NEXT: global.get globalI32
44+
; CHECK-NEXT: i32.const 65535
45+
; CHECK-NEXT: i32.and
46+
; CHECK-NEXT: # fallthrough-return
47+
%v = load i16, ptr addrspace(1) @globalI32
48+
%e = zext i16 %v to i32
49+
ret i32 %e
50+
}
51+
52+
define i32 @sext_i16_i32() {
53+
; CHECK-LABEL: sext_i16_i32:
54+
; CHECK: .functype sext_i16_i32 () -> (i32)
55+
; CHECK-NEXT: # %bb.0:
56+
; CHECK-NEXT: global.get globalI32
57+
; CHECK-NEXT: i32.extend16_s
58+
; CHECK-NEXT: # fallthrough-return
59+
%v = load i16, ptr addrspace(1) @globalI32
60+
%e = sext i16 %v to i32
61+
ret i32 %e
62+
}
63+
64+
65+
define i64 @zext_i8_i64() {
66+
; CHECK-LABEL: zext_i8_i64:
67+
; CHECK: .functype zext_i8_i64 () -> (i64)
68+
; CHECK-NEXT: # %bb.0:
69+
; CHECK-NEXT: global.get globalI64
70+
; CHECK-NEXT: i64.const 255
71+
; CHECK-NEXT: i64.and
72+
; CHECK-NEXT: # fallthrough-return
73+
%v = load i8, ptr addrspace(1) @globalI64
74+
%e = zext i8 %v to i64
75+
ret i64 %e
76+
}
77+
78+
define i64 @sext_i8_i64() {
79+
; CHECK-LABEL: sext_i8_i64:
80+
; CHECK: .functype sext_i8_i64 () -> (i64)
81+
; CHECK-NEXT: # %bb.0:
82+
; CHECK-NEXT: global.get globalI64
83+
; CHECK-NEXT: i64.extend8_s
84+
; CHECK-NEXT: # fallthrough-return
85+
%v = load i8, ptr addrspace(1) @globalI64
86+
%e = sext i8 %v to i64
87+
ret i64 %e
88+
}
89+
90+
define i64 @zext_i16_i64() {
91+
; CHECK-LABEL: zext_i16_i64:
92+
; CHECK: .functype zext_i16_i64 () -> (i64)
93+
; CHECK-NEXT: # %bb.0:
94+
; CHECK-NEXT: global.get globalI64
95+
; CHECK-NEXT: i64.const 65535
96+
; CHECK-NEXT: i64.and
97+
; CHECK-NEXT: # fallthrough-return
98+
%v = load i16, ptr addrspace(1) @globalI64
99+
%e = zext i16 %v to i64
100+
ret i64 %e
101+
}
102+
103+
define i64 @sext_i16_i64() {
104+
; CHECK-LABEL: sext_i16_i64:
105+
; CHECK: .functype sext_i16_i64 () -> (i64)
106+
; CHECK-NEXT: # %bb.0:
107+
; CHECK-NEXT: global.get globalI64
108+
; CHECK-NEXT: i64.extend16_s
109+
; CHECK-NEXT: # fallthrough-return
110+
%v = load i16, ptr addrspace(1) @globalI64
111+
%e = sext i16 %v to i64
112+
ret i64 %e
113+
}
114+
115+
define i64 @zext_i32_i64() {
116+
; CHECK-LABEL: zext_i32_i64:
117+
; CHECK: .functype zext_i32_i64 () -> (i64)
118+
; CHECK-NEXT: # %bb.0:
119+
; CHECK-NEXT: global.get globalI64
120+
; CHECK-NEXT: i64.const 4294967295
121+
; CHECK-NEXT: i64.and
122+
; CHECK-NEXT: # fallthrough-return
123+
%v = load i32, ptr addrspace(1) @globalI64
124+
%e = zext i32 %v to i64
125+
ret i64 %e
126+
}
127+
128+
define i64 @sext_i32_i64() {
129+
; CHECK-LABEL: sext_i32_i64:
130+
; CHECK: .functype sext_i32_i64 () -> (i64)
131+
; CHECK-NEXT: # %bb.0:
132+
; CHECK-NEXT: global.get globalI64
133+
; CHECK-NEXT: i64.extend32_s
134+
; CHECK-NEXT: # fallthrough-return
135+
%v = load i32, ptr addrspace(1) @globalI64
136+
%e = sext i32 %v to i64
137+
ret i64 %e
138+
}
139+
140+
141+
define i64 @load_i64_from_i32() {
142+
; CHECK-LABEL: load_i64_from_i32:
143+
; CHECK: .functype load_i64_from_i32 () -> (i64)
144+
; CHECK-NEXT: # %bb.0:
145+
; CHECK-NEXT: global.get globalI32
146+
; CHECK-NEXT: i64.extend_i32_u
147+
; CHECK-NEXT: # fallthrough-return
148+
%v = load i64, ptr addrspace(1) @globalI32
149+
ret i64 %v
150+
}
151+
152+
define i32 @load_i32_from_i64() {
153+
; CHECK-LABEL: load_i32_from_i64:
154+
; CHECK: .functype load_i32_from_i64 () -> (i32)
155+
; CHECK-NEXT: # %bb.0:
156+
; CHECK-NEXT: global.get globalI64
157+
; CHECK-NEXT: i32.wrap_i64
158+
; CHECK-NEXT: # fallthrough-return
159+
%v = load i32, ptr addrspace(1) @globalI64
160+
ret i32 %v
161+
}
162+
163+
define i8 @load_i8() {
164+
; CHECK-LABEL: load_i8:
165+
; CHECK: .functype load_i8 () -> (i32)
166+
; CHECK-NEXT: # %bb.0:
167+
; CHECK-NEXT: global.get globalI8
168+
; CHECK-NEXT: # fallthrough-return
169+
%v = load i8, ptr addrspace(1) @globalI8
170+
ret i8 %v
171+
}
172+
173+
define i64 @load_i16_from_i8_zext_to_i64() {
174+
; CHECK-LABEL: load_i16_from_i8_zext_to_i64:
175+
; CHECK: .functype load_i16_from_i8_zext_to_i64 () -> (i64)
176+
; CHECK-NEXT: # %bb.0:
177+
; CHECK-NEXT: global.get globalI8
178+
; CHECK-NEXT: i64.extend_i32_u
179+
; CHECK-NEXT: i64.const 65535
180+
; CHECK-NEXT: i64.and
181+
; CHECK-NEXT: # fallthrough-return
182+
%v = load i16, ptr addrspace(1) @globalI8
183+
%e = zext i16 %v to i64
184+
ret i64 %e
185+
}

0 commit comments

Comments
 (0)