Skip to content

Commit d7086af

Browse files
pmatoswingo
authored andcommitted
[WebAssembly] Support for WebAssembly globals in LLVM IR
This patch adds support for WebAssembly globals in LLVM IR, representing them as pointers to global values, in a non-default, non-integral address space. Instruction selection legalizes loads and stores to these pointers to new WebAssemblyISD nodes GLOBAL_GET and GLOBAL_SET. Once the lowering creates the new nodes, tablegen pattern matches those and converts them to Wasm global.get/set of the appropriate type. Based on work by Paulo Matos in https://reviews.llvm.org/D95425. Reviewed By: pmatos Differential Revision: https://reviews.llvm.org/D101608
1 parent 04adfb6 commit d7086af

File tree

12 files changed

+271
-47
lines changed

12 files changed

+271
-47
lines changed

clang/lib/Basic/Targets/WebAssembly.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssembly32TargetInfo
147147
explicit WebAssembly32TargetInfo(const llvm::Triple &T,
148148
const TargetOptions &Opts)
149149
: WebAssemblyTargetInfo(T, Opts) {
150-
resetDataLayout("e-m:e-p:32:32-i64:64-n32:64-S128");
150+
resetDataLayout("e-m:e-p:32:32-i64:64-n32:64-S128-ni:1");
151151
}
152152

153153
protected:
@@ -166,7 +166,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssembly64TargetInfo
166166
SizeType = UnsignedLong;
167167
PtrDiffType = SignedLong;
168168
IntPtrType = SignedLong;
169-
resetDataLayout("e-m:e-p:64:64-i64:64-n32:64-S128");
169+
resetDataLayout("e-m:e-p:64:64-i64:64-n32:64-S128-ni:1");
170170
}
171171

172172
protected:

clang/test/CodeGen/target-data.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,11 @@
108108

109109
// RUN: %clang_cc1 -triple wasm32-unknown-unknown -o - -emit-llvm %s | \
110110
// RUN: FileCheck %s -check-prefix=WEBASSEMBLY32
111-
// WEBASSEMBLY32: target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
111+
// WEBASSEMBLY32: target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1"
112112

113113
// RUN: %clang_cc1 -triple wasm64-unknown-unknown -o - -emit-llvm %s | \
114114
// RUN: FileCheck %s -check-prefix=WEBASSEMBLY64
115-
// WEBASSEMBLY64: target datalayout = "e-m:e-p:64:64-i64:64-n32:64-S128"
115+
// WEBASSEMBLY64: target datalayout = "e-m:e-p:64:64-i64:64-n32:64-S128-ni:1"
116116

117117
// RUN: %clang_cc1 -triple lanai-unknown-unknown -o - -emit-llvm %s | \
118118
// RUN: FileCheck %s -check-prefix=LANAI

llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,26 @@ class WebAssemblySubtarget;
2828

2929
namespace WebAssembly {
3030

31+
enum WasmAddressSpace : unsigned {
32+
// Default address space, for pointers to linear memory (stack, heap, data).
33+
WASM_ADDRESS_SPACE_DEFAULT = 0,
34+
// A non-integral address space for pointers to named objects outside of
35+
// linear memory: WebAssembly globals or WebAssembly locals. Loads and stores
36+
// to these pointers are lowered to global.get / global.set or local.get /
37+
// local.set, as appropriate.
38+
WASM_ADDRESS_SPACE_WASM_VAR = 1
39+
};
40+
41+
inline bool isDefaultAddressSpace(unsigned AS) {
42+
return AS == WASM_ADDRESS_SPACE_DEFAULT;
43+
}
44+
inline bool isWasmVarAddressSpace(unsigned AS) {
45+
return AS == WASM_ADDRESS_SPACE_WASM_VAR;
46+
}
47+
inline bool isValidAddressSpace(unsigned AS) {
48+
return isDefaultAddressSpace(AS) || isWasmVarAddressSpace(AS);
49+
}
50+
3151
bool isChild(const MachineInstr &MI, const WebAssemblyFunctionInfo &MFI);
3252
bool mayThrow(const MachineInstr &MI);
3353

llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,8 @@ bool WebAssemblyFastISel::selectLoad(const Instruction *I) {
11821182
const auto *Load = cast<LoadInst>(I);
11831183
if (Load->isAtomic())
11841184
return false;
1185+
if (!WebAssembly::isDefaultAddressSpace(Load->getPointerAddressSpace()))
1186+
return false;
11851187
if (!Subtarget->hasSIMD128() && Load->getType()->isVectorTy())
11861188
return false;
11871189

@@ -1240,6 +1242,8 @@ bool WebAssemblyFastISel::selectStore(const Instruction *I) {
12401242
const auto *Store = cast<StoreInst>(I);
12411243
if (Store->isAtomic())
12421244
return false;
1245+
if (!WebAssembly::isDefaultAddressSpace(Store->getPointerAddressSpace()))
1246+
return false;
12431247
if (!Subtarget->hasSIMD128() &&
12441248
Store->getValueOperand()->getType()->isVectorTy())
12451249
return false;

llvm/lib/Target/WebAssembly/WebAssemblyISD.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ HANDLE_NODETYPE(RETURN)
1919
HANDLE_NODETYPE(ARGUMENT)
2020
// A wrapper node for TargetExternalSymbol, TargetGlobalAddress, and MCSymbol
2121
HANDLE_NODETYPE(Wrapper)
22-
// A special wapper used in PIC code for __memory_base/__table_base relcative
22+
// A special wapper used in PIC code for __memory_base/__table_base relative
2323
// access.
2424
HANDLE_NODETYPE(WrapperPIC)
2525
HANDLE_NODETYPE(BR_IF)
@@ -44,3 +44,5 @@ HANDLE_NODETYPE(MEMORY_FILL)
4444

4545
// Memory intrinsics
4646
HANDLE_MEM_NODETYPE(LOAD_SPLAT)
47+
HANDLE_MEM_NODETYPE(GLOBAL_GET)
48+
HANDLE_MEM_NODETYPE(GLOBAL_SET)

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717
#include "WebAssemblyMachineFunctionInfo.h"
1818
#include "WebAssemblySubtarget.h"
1919
#include "WebAssemblyTargetMachine.h"
20-
#include "llvm/CodeGen/Analysis.h"
2120
#include "llvm/CodeGen/CallingConvLower.h"
2221
#include "llvm/CodeGen/MachineInstrBuilder.h"
2322
#include "llvm/CodeGen/MachineJumpTableInfo.h"
2423
#include "llvm/CodeGen/MachineModuleInfo.h"
2524
#include "llvm/CodeGen/MachineRegisterInfo.h"
2625
#include "llvm/CodeGen/SelectionDAG.h"
26+
#include "llvm/CodeGen/SelectionDAGNodes.h"
2727
#include "llvm/CodeGen/WasmEHFuncInfo.h"
2828
#include "llvm/IR/DiagnosticInfo.h"
2929
#include "llvm/IR/DiagnosticPrinter.h"
@@ -69,6 +69,20 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
6969
// Compute derived properties from the register classes.
7070
computeRegisterProperties(Subtarget->getRegisterInfo());
7171

72+
// Transform loads and stores to pointers in address space 1 to loads and
73+
// stores to WebAssembly global variables, outside linear memory.
74+
for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) {
75+
setOperationAction(ISD::LOAD, T, Custom);
76+
setOperationAction(ISD::STORE, T, Custom);
77+
}
78+
if (Subtarget->hasSIMD128()) {
79+
for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64,
80+
MVT::v2f64}) {
81+
setOperationAction(ISD::LOAD, T, Custom);
82+
setOperationAction(ISD::STORE, T, Custom);
83+
}
84+
}
85+
7286
setOperationAction(ISD::GlobalAddress, MVTPtr, Custom);
7387
setOperationAction(ISD::GlobalTLSAddress, MVTPtr, Custom);
7488
setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom);
@@ -1248,9 +1262,63 @@ SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op,
12481262
case ISD::FP_TO_SINT_SAT:
12491263
case ISD::FP_TO_UINT_SAT:
12501264
return LowerFP_TO_INT_SAT(Op, DAG);
1265+
case ISD::LOAD:
1266+
return LowerLoad(Op, DAG);
1267+
case ISD::STORE:
1268+
return LowerStore(Op, DAG);
12511269
}
12521270
}
12531271

1272+
static bool IsWebAssemblyGlobal(SDValue Op) {
1273+
if (const GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Op))
1274+
return WebAssembly::isWasmVarAddressSpace(GA->getAddressSpace());
1275+
1276+
return false;
1277+
}
1278+
1279+
SDValue WebAssemblyTargetLowering::LowerStore(SDValue Op,
1280+
SelectionDAG &DAG) const {
1281+
SDLoc DL(Op);
1282+
StoreSDNode *SN = cast<StoreSDNode>(Op.getNode());
1283+
const SDValue &Value = SN->getValue();
1284+
const SDValue &Base = SN->getBasePtr();
1285+
const SDValue &Offset = SN->getOffset();
1286+
1287+
if (IsWebAssemblyGlobal(Base)) {
1288+
if (!Offset->isUndef())
1289+
report_fatal_error("unexpected offset when storing to webassembly global",
1290+
false);
1291+
1292+
SDVTList Tys = DAG.getVTList(MVT::Other);
1293+
SDValue Ops[] = {SN->getChain(), Value, Base};
1294+
return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_SET, DL, Tys, Ops,
1295+
SN->getMemoryVT(), SN->getMemOperand());
1296+
}
1297+
1298+
return Op;
1299+
}
1300+
1301+
SDValue WebAssemblyTargetLowering::LowerLoad(SDValue Op,
1302+
SelectionDAG &DAG) const {
1303+
SDLoc DL(Op);
1304+
LoadSDNode *LN = cast<LoadSDNode>(Op.getNode());
1305+
const SDValue &Base = LN->getBasePtr();
1306+
const SDValue &Offset = LN->getOffset();
1307+
1308+
if (IsWebAssemblyGlobal(Base)) {
1309+
if (!Offset->isUndef())
1310+
report_fatal_error(
1311+
"unexpected offset when loading from webassembly global", false);
1312+
1313+
SDVTList Tys = DAG.getVTList(LN->getValueType(0), MVT::Other);
1314+
SDValue Ops[] = {LN->getChain(), Base};
1315+
return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_GET, DL, Tys, Ops,
1316+
LN->getMemoryVT(), LN->getMemOperand());
1317+
}
1318+
1319+
return Op;
1320+
}
1321+
12541322
SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op,
12551323
SelectionDAG &DAG) const {
12561324
SDValue Src = Op.getOperand(2);
@@ -1369,8 +1437,8 @@ SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op,
13691437
EVT VT = Op.getValueType();
13701438
assert(GA->getTargetFlags() == 0 &&
13711439
"Unexpected target flags on generic GlobalAddressSDNode");
1372-
if (GA->getAddressSpace() != 0)
1373-
fail(DL, DAG, "WebAssembly only expects the 0 address space");
1440+
if (!WebAssembly::isValidAddressSpace(GA->getAddressSpace()))
1441+
fail(DL, DAG, "Invalid address space for WebAssembly target");
13741442

13751443
unsigned OperandFlags = 0;
13761444
if (isPositionIndependent()) {

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ class WebAssemblyTargetLowering final : public TargetLowering {
120120
SDValue LowerAccessVectorElement(SDValue Op, SelectionDAG &DAG) const;
121121
SDValue LowerShift(SDValue Op, SelectionDAG &DAG) const;
122122
SDValue LowerFP_TO_INT_SAT(SDValue Op, SelectionDAG &DAG) const;
123+
SDValue LowerLoad(SDValue Op, SelectionDAG &DAG) const;
124+
SDValue LowerStore(SDValue Op, SelectionDAG &DAG) const;
123125

124126
// Custom DAG combine hooks
125127
SDValue

llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ def SDT_WebAssemblyWrapperPIC : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
7979
SDTCisPtrTy<0>]>;
8080
def SDT_WebAssemblyThrow : SDTypeProfile<0, -1, []>;
8181
def SDT_WebAssemblyCatch : SDTypeProfile<1, 1, [SDTCisPtrTy<0>]>;
82+
def SDT_WebAssemblyGlobalGet : SDTypeProfile<1, 1, [SDTCisPtrTy<1>]>;
83+
def SDT_WebAssemblyGlobalSet : SDTypeProfile<0, 2, [SDTCisPtrTy<1>]>;
8284

8385
//===----------------------------------------------------------------------===//
8486
// WebAssembly-specific DAG Nodes.
@@ -106,6 +108,12 @@ def WebAssemblythrow : SDNode<"WebAssemblyISD::THROW", SDT_WebAssemblyThrow,
106108
[SDNPHasChain, SDNPVariadic]>;
107109
def WebAssemblycatch : SDNode<"WebAssemblyISD::CATCH", SDT_WebAssemblyCatch,
108110
[SDNPHasChain, SDNPSideEffect]>;
111+
def WebAssemblyglobal_get :
112+
SDNode<"WebAssemblyISD::GLOBAL_GET", SDT_WebAssemblyGlobalGet,
113+
[SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>;
114+
def WebAssemblyglobal_set :
115+
SDNode<"WebAssemblyISD::GLOBAL_SET", SDT_WebAssemblyGlobalSet,
116+
[SDNPHasChain, SDNPMayStore, SDNPMemOperand]>;
109117

110118
//===----------------------------------------------------------------------===//
111119
// WebAssembly-specific Operands.
@@ -241,12 +249,12 @@ include "WebAssemblyInstrFormats.td"
241249
// Additional instructions.
242250
//===----------------------------------------------------------------------===//
243251

244-
multiclass ARGUMENT<WebAssemblyRegClass reg, ValueType vt> {
252+
multiclass ARGUMENT<WebAssemblyRegClass rc, ValueType vt> {
245253
let hasSideEffects = 1, isCodeGenOnly = 1, Defs = []<Register>,
246254
Uses = [ARGUMENTS] in
247255
defm ARGUMENT_#vt :
248-
I<(outs reg:$res), (ins i32imm:$argno), (outs), (ins i32imm:$argno),
249-
[(set (vt reg:$res), (WebAssemblyargument timm:$argno))]>;
256+
I<(outs rc:$res), (ins i32imm:$argno), (outs), (ins i32imm:$argno),
257+
[(set (vt rc:$res), (WebAssemblyargument timm:$argno))]>;
250258
}
251259
defm "": ARGUMENT<I32, i32>;
252260
defm "": ARGUMENT<I64, i64>;
@@ -257,66 +265,74 @@ defm "": ARGUMENT<EXTERNREF, externref>;
257265

258266
// local.get and local.set are not generated by instruction selection; they
259267
// are implied by virtual register uses and defs.
260-
multiclass LOCAL<WebAssemblyRegClass vt, Operand global_op> {
268+
multiclass LOCAL<WebAssemblyRegClass rc, Operand global_op> {
261269
let hasSideEffects = 0 in {
262270
// COPY is not an actual instruction in wasm, but since we allow local.get and
263271
// local.set to be implicit during most of codegen, we can have a COPY which
264272
// is actually a no-op because all the work is done in the implied local.get
265273
// and local.set. COPYs are eliminated (and replaced with
266274
// local.get/local.set) in the ExplicitLocals pass.
267275
let isAsCheapAsAMove = 1, isCodeGenOnly = 1 in
268-
defm COPY_#vt : I<(outs vt:$res), (ins vt:$src), (outs), (ins), [],
276+
defm COPY_#rc : I<(outs rc:$res), (ins rc:$src), (outs), (ins), [],
269277
"local.copy\t$res, $src", "local.copy">;
270278

271279
// TEE is similar to COPY, but writes two copies of its result. Typically
272280
// this would be used to stackify one result and write the other result to a
273281
// local.
274282
let isAsCheapAsAMove = 1, isCodeGenOnly = 1 in
275-
defm TEE_#vt : I<(outs vt:$res, vt:$also), (ins vt:$src), (outs), (ins), [],
283+
defm TEE_#rc : I<(outs rc:$res, rc:$also), (ins rc:$src), (outs), (ins), [],
276284
"local.tee\t$res, $also, $src", "local.tee">;
277285

278286
// This is the actual local.get instruction in wasm. These are made explicit
279287
// by the ExplicitLocals pass. It has mayLoad because it reads from a wasm
280288
// local, which is a side effect not otherwise modeled in LLVM.
281289
let mayLoad = 1, isAsCheapAsAMove = 1 in
282-
defm LOCAL_GET_#vt : I<(outs vt:$res), (ins local_op:$local),
290+
defm LOCAL_GET_#rc : I<(outs rc:$res), (ins local_op:$local),
283291
(outs), (ins local_op:$local), [],
284292
"local.get\t$res, $local", "local.get\t$local", 0x20>;
285293

286294
// This is the actual local.set instruction in wasm. These are made explicit
287295
// by the ExplicitLocals pass. It has mayStore because it writes to a wasm
288296
// local, which is a side effect not otherwise modeled in LLVM.
289297
let mayStore = 1, isAsCheapAsAMove = 1 in
290-
defm LOCAL_SET_#vt : I<(outs), (ins local_op:$local, vt:$src),
298+
defm LOCAL_SET_#rc : I<(outs), (ins local_op:$local, rc:$src),
291299
(outs), (ins local_op:$local), [],
292300
"local.set\t$local, $src", "local.set\t$local", 0x21>;
293301

294302
// This is the actual local.tee instruction in wasm. TEEs are turned into
295303
// LOCAL_TEEs by the ExplicitLocals pass. It has mayStore for the same reason
296304
// as LOCAL_SET.
297305
let mayStore = 1, isAsCheapAsAMove = 1 in
298-
defm LOCAL_TEE_#vt : I<(outs vt:$res), (ins local_op:$local, vt:$src),
306+
defm LOCAL_TEE_#rc : I<(outs rc:$res), (ins local_op:$local, rc:$src),
299307
(outs), (ins local_op:$local), [],
300308
"local.tee\t$res, $local, $src", "local.tee\t$local",
301309
0x22>;
302310

303311
// Unused values must be dropped in some contexts.
304-
defm DROP_#vt : I<(outs), (ins vt:$src), (outs), (ins), [],
312+
defm DROP_#rc : I<(outs), (ins rc:$src), (outs), (ins), [],
305313
"drop\t$src", "drop", 0x1a>;
306314

307315
let mayLoad = 1 in
308-
defm GLOBAL_GET_#vt : I<(outs vt:$res), (ins global_op:$local),
309-
(outs), (ins global_op:$local), [],
310-
"global.get\t$res, $local", "global.get\t$local",
316+
defm GLOBAL_GET_#rc : I<(outs rc:$res), (ins global_op:$addr),
317+
(outs), (ins global_op:$addr), [],
318+
"global.get\t$res, $addr", "global.get\t$addr",
311319
0x23>;
312320

313321
let mayStore = 1 in
314-
defm GLOBAL_SET_#vt : I<(outs), (ins global_op:$local, vt:$src),
315-
(outs), (ins global_op:$local), [],
316-
"global.set\t$local, $src", "global.set\t$local",
322+
defm GLOBAL_SET_#rc : I<(outs), (ins global_op:$addr, rc:$src),
323+
(outs), (ins global_op:$addr), [],
324+
"global.set\t$addr, $src", "global.set\t$addr",
317325
0x24>;
318326

319-
} // hasSideEffects = 0
327+
} // hasSideEffects = 0
328+
foreach vt = rc.RegTypes in {
329+
def : Pat<(vt (WebAssemblyglobal_get
330+
(WebAssemblywrapper tglobaladdr:$addr))),
331+
(!cast<NI>("GLOBAL_GET_" # rc) tglobaladdr:$addr)>;
332+
def : Pat<(WebAssemblyglobal_set
333+
vt:$src, (WebAssemblywrapper tglobaladdr:$addr)),
334+
(!cast<NI>("GLOBAL_SET_" # rc) tglobaladdr:$addr, vt:$src)>;
335+
}
320336
}
321337
defm "" : LOCAL<I32, global_op32>;
322338
defm "" : LOCAL<I64, global_op64>; // 64-bit only needed for pointers.

llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,29 @@
1111
///
1212
//===----------------------------------------------------------------------===//
1313

14-
multiclass REF_I<WebAssemblyRegClass reg, ValueType vt> {
15-
defm REF_NULL_#reg : I<(outs reg:$res), (ins HeapType:$heaptype),
16-
(outs), (ins HeapType:$heaptype),
17-
[],
18-
"ref.null\t$res, $heaptype",
19-
"ref.null\t$heaptype",
20-
0xd0>,
21-
Requires<[HasReferenceTypes]>;
22-
defm SELECT_#reg: I<(outs reg:$dst), (ins reg:$lhs, reg:$rhs, I32:$cond),
23-
(outs), (ins),
24-
[(set reg:$dst,
25-
(select I32:$cond, reg:$lhs, reg:$rhs))],
26-
vt#".select\t$dst, $lhs, $rhs, $cond",
27-
vt#".select", 0x1b>,
28-
Requires<[HasReferenceTypes]>;
14+
multiclass REF_I<WebAssemblyRegClass rc, ValueType vt> {
15+
defm REF_NULL_#rc : I<(outs rc:$res), (ins HeapType:$heaptype),
16+
(outs), (ins HeapType:$heaptype),
17+
[],
18+
"ref.null\t$res, $heaptype",
19+
"ref.null\t$heaptype",
20+
0xd0>,
21+
Requires<[HasReferenceTypes]>;
22+
defm SELECT_#rc: I<(outs rc:$dst), (ins rc:$lhs, rc:$rhs, I32:$cond),
23+
(outs), (ins),
24+
[(set rc:$dst,
25+
(select I32:$cond, rc:$lhs, rc:$rhs))],
26+
vt#".select\t$dst, $lhs, $rhs, $cond",
27+
vt#".select", 0x1b>,
28+
Requires<[HasReferenceTypes]>;
2929
}
3030

3131
defm "" : REF_I<FUNCREF, funcref>;
3232
defm "" : REF_I<EXTERNREF, externref>;
3333

34-
foreach reg = [FUNCREF, EXTERNREF] in {
35-
def : Pat<(select (i32 (setne I32:$cond, 0)), reg:$lhs, reg:$rhs),
36-
(!cast<Instruction>("SELECT_"#reg) reg:$lhs, reg:$rhs, I32:$cond)>;
37-
def : Pat<(select (i32 (seteq I32:$cond, 0)), reg:$lhs, reg:$rhs),
38-
(!cast<Instruction>("SELECT_"#reg) reg:$rhs, reg:$lhs, I32:$cond)>;
34+
foreach rc = [FUNCREF, EXTERNREF] in {
35+
def : Pat<(select (i32 (setne I32:$cond, 0)), rc:$lhs, rc:$rhs),
36+
(!cast<Instruction>("SELECT_"#rc) rc:$lhs, rc:$rhs, I32:$cond)>;
37+
def : Pat<(select (i32 (seteq I32:$cond, 0)), rc:$lhs, rc:$rhs),
38+
(!cast<Instruction>("SELECT_"#rc) rc:$rhs, rc:$lhs, I32:$cond)>;
3939
}

0 commit comments

Comments
 (0)