Skip to content

Commit c2293b3

Browse files
authored
[WebAssembly] Implement the wide-arithmetic proposal (#111598)
This commit implements the [wide-arithmetic] proposal which has recently reached phase 2 in the WebAssembly proposals process. The goal here is to implement support in LLVM for emitting these instructions which are gated behind a new feature flag by default. A new `wide-arithmetic` feature flag is introduced which gates these four new instructions from being emitted. Emission of each instruction itself is relatively simple given LLVM's preexisting lowering rules and infrastructure. The main gotcha is that due to the multi-result nature of all of these instructions it needed the lowerings to be implemented in C++ rather than in TableGen. [wide-arithmetic]: https://github.com/WebAssembly/wide-arithmetic
1 parent c99f395 commit c2293b3

File tree

13 files changed

+297
-0
lines changed

13 files changed

+297
-0
lines changed

clang/include/clang/Driver/Options.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5106,6 +5106,8 @@ def msimd128 : Flag<["-"], "msimd128">, Group<m_wasm_Features_Group>;
51065106
def mno_simd128 : Flag<["-"], "mno-simd128">, Group<m_wasm_Features_Group>;
51075107
def mtail_call : Flag<["-"], "mtail-call">, Group<m_wasm_Features_Group>;
51085108
def mno_tail_call : Flag<["-"], "mno-tail-call">, Group<m_wasm_Features_Group>;
5109+
def mwide_arithmetic : Flag<["-"], "mwide-arithmetic">, Group<m_wasm_Features_Group>;
5110+
def mno_wide_arithmetic : Flag<["-"], "mno-wide-arithmetic">, Group<m_wasm_Features_Group>;
51095111
def mexec_model_EQ : Joined<["-"], "mexec-model=">, Group<m_wasm_Features_Driver_Group>,
51105112
Values<"command,reactor">,
51115113
HelpText<"Execution model (WebAssembly only)">,

clang/lib/Basic/Targets/WebAssembly.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const {
5959
.Case("sign-ext", HasSignExt)
6060
.Case("simd128", SIMDLevel >= SIMD128)
6161
.Case("tail-call", HasTailCall)
62+
.Case("wide-arithmetic", HasWideArithmetic)
6263
.Default(false);
6364
}
6465

@@ -102,6 +103,8 @@ void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts,
102103
Builder.defineMacro("__wasm_simd128__");
103104
if (HasTailCall)
104105
Builder.defineMacro("__wasm_tail_call__");
106+
if (HasWideArithmetic)
107+
Builder.defineMacro("__wasm_wide_arithmetic__");
105108

106109
Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1");
107110
Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2");
@@ -166,6 +169,7 @@ bool WebAssemblyTargetInfo::initFeatureMap(
166169
Features["multimemory"] = true;
167170
Features["nontrapping-fptoint"] = true;
168171
Features["tail-call"] = true;
172+
Features["wide-arithmetic"] = true;
169173
setSIMDLevel(Features, RelaxedSIMD, true);
170174
};
171175
if (CPU == "generic") {
@@ -293,6 +297,14 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
293297
HasTailCall = false;
294298
continue;
295299
}
300+
if (Feature == "+wide-arithmetic") {
301+
HasWideArithmetic = true;
302+
continue;
303+
}
304+
if (Feature == "-wide-arithmetic") {
305+
HasWideArithmetic = false;
306+
continue;
307+
}
296308

297309
Diags.Report(diag::err_opt_not_valid_with_opt)
298310
<< Feature << "-target-feature";

clang/lib/Basic/Targets/WebAssembly.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
6565
bool HasReferenceTypes = false;
6666
bool HasSignExt = false;
6767
bool HasTailCall = false;
68+
bool HasWideArithmetic = false;
6869

6970
std::string ABI;
7071

clang/test/Driver/wasm-features.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,9 @@
9494

9595
// TAIL-CALL: "-target-feature" "+tail-call"
9696
// NO-TAIL-CALL: "-target-feature" "-tail-call"
97+
98+
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mwide-arithmetic 2>&1 | FileCheck %s -check-prefix=WIDE-ARITH
99+
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-wide-arithmetic 2>&1 | FileCheck %s -check-prefix=NO-WIDE-ARITH
100+
101+
// WIDE-ARITH: "-target-feature" "+wide-arithmetic"
102+
// NO-WIDE-ARITH: "-target-feature" "-wide-arithmetic"

clang/test/Preprocessor/wasm-target-features.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@
154154
// MVP-NOT: #define __wasm_sign_ext__ 1{{$}}
155155
// MVP-NOT: #define __wasm_simd128__ 1{{$}}
156156
// MVP-NOT: #define __wasm_tail_call__ 1{{$}}
157+
// MVP-NOT: #define __wasm_wide_arithmetic__ 1{{$}}
157158

158159
// RUN: %clang -E -dM %s -o - 2>&1 \
159160
// RUN: -target wasm32-unknown-unknown -mcpu=generic \
@@ -184,6 +185,7 @@
184185
// GENERIC-NOT: #define __wasm_relaxed_simd__ 1{{$}}
185186
// GENERIC-NOT: #define __wasm_simd128__ 1{{$}}
186187
// GENERIC-NOT: #define __wasm_tail_call__ 1{{$}}
188+
// GENERIC-NOT: #define __wasm_wide_arithmetic__ 1{{$}}
187189

188190
// RUN: %clang -E -dM %s -o - 2>&1 \
189191
// RUN: -target wasm32-unknown-unknown -mcpu=bleeding-edge \
@@ -206,6 +208,7 @@
206208
// BLEEDING-EDGE-INCLUDE-DAG: #define __wasm_sign_ext__ 1{{$}}
207209
// BLEEDING-EDGE-INCLUDE-DAG: #define __wasm_simd128__ 1{{$}}
208210
// BLEEDING-EDGE-INCLUDE-DAG: #define __wasm_tail_call__ 1{{$}}
211+
// BLEEDING-EDGE-INCLUDE-DAG: #define __wasm_wide_arithmetic__ 1{{$}}
209212

210213
// RUN: %clang -E -dM %s -o - 2>&1 \
211214
// RUN: -target wasm32-unknown-unknown -mcpu=bleeding-edge -mno-simd128 \
@@ -215,3 +218,12 @@
215218
// RUN: | FileCheck %s -check-prefix=BLEEDING-EDGE-NO-SIMD128
216219
//
217220
// BLEEDING-EDGE-NO-SIMD128-NOT: #define __wasm_simd128__ 1{{$}}
221+
222+
// RUN: %clang -E -dM %s -o - 2>&1 \
223+
// RUN: -target wasm32-unknown-unknown -mwide-arithmetic \
224+
// RUN: | FileCheck %s -check-prefix=WIDE-ARITHMETIC
225+
// RUN: %clang -E -dM %s -o - 2>&1 \
226+
// RUN: -target wasm64-unknown-unknown -mwide-arithmetic \
227+
// RUN: | FileCheck %s -check-prefix=WIDE-ARITHMETIC
228+
//
229+
// WIDE-ARITHMETIC: #define __wasm_wide_arithmetic__ 1{{$}}

llvm/lib/Target/WebAssembly/WebAssembly.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ def FeatureTailCall :
7878
SubtargetFeature<"tail-call", "HasTailCall", "true",
7979
"Enable tail call instructions">;
8080

81+
def FeatureWideArithmetic :
82+
SubtargetFeature<"wide-arithmetic", "HasWideArithmetic", "true",
83+
"Enable wide-arithmetic instructions">;
84+
8185
//===----------------------------------------------------------------------===//
8286
// Architectures.
8387
//===----------------------------------------------------------------------===//

llvm/lib/Target/WebAssembly/WebAssemblyISD.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ HANDLE_NODETYPE(TRUNC_SAT_ZERO_U)
4444
HANDLE_NODETYPE(DEMOTE_ZERO)
4545
HANDLE_NODETYPE(MEMORY_COPY)
4646
HANDLE_NODETYPE(MEMORY_FILL)
47+
HANDLE_NODETYPE(I64_ADD128)
48+
HANDLE_NODETYPE(I64_SUB128)
49+
HANDLE_NODETYPE(I64_MUL_WIDE_S)
50+
HANDLE_NODETYPE(I64_MUL_WIDE_U)
4751

4852
// Memory intrinsics
4953
HANDLE_MEM_NODETYPE(GLOBAL_GET)

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,13 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
167167
setOperationAction(Op, T, Expand);
168168
}
169169

170+
if (Subtarget->hasWideArithmetic()) {
171+
setOperationAction(ISD::ADD, MVT::i128, Custom);
172+
setOperationAction(ISD::SUB, MVT::i128, Custom);
173+
setOperationAction(ISD::SMUL_LOHI, MVT::i64, Custom);
174+
setOperationAction(ISD::UMUL_LOHI, MVT::i64, Custom);
175+
}
176+
170177
if (Subtarget->hasNontrappingFPToInt())
171178
for (auto Op : {ISD::FP_TO_SINT_SAT, ISD::FP_TO_UINT_SAT})
172179
for (auto T : {MVT::i32, MVT::i64})
@@ -1419,6 +1426,10 @@ void WebAssemblyTargetLowering::ReplaceNodeResults(
14191426
// Do not add any results, signifying that N should not be custom lowered.
14201427
// EXTEND_VECTOR_INREG is implemented for some vectors, but not all.
14211428
break;
1429+
case ISD::ADD:
1430+
case ISD::SUB:
1431+
Results.push_back(Replace128Op(N, DAG));
1432+
break;
14221433
default:
14231434
llvm_unreachable(
14241435
"ReplaceNodeResults not implemented for this op for WebAssembly!");
@@ -1495,6 +1506,9 @@ SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op,
14951506
return DAG.UnrollVectorOp(Op.getNode());
14961507
case ISD::CLEAR_CACHE:
14971508
report_fatal_error("llvm.clear_cache is not supported on wasm");
1509+
case ISD::SMUL_LOHI:
1510+
case ISD::UMUL_LOHI:
1511+
return LowerMUL_LOHI(Op, DAG);
14981512
}
14991513
}
15001514

@@ -1593,6 +1607,63 @@ SDValue WebAssemblyTargetLowering::LowerLoad(SDValue Op,
15931607
return Op;
15941608
}
15951609

1610+
SDValue WebAssemblyTargetLowering::LowerMUL_LOHI(SDValue Op,
1611+
SelectionDAG &DAG) const {
1612+
assert(Subtarget->hasWideArithmetic());
1613+
assert(Op.getValueType() == MVT::i64);
1614+
SDLoc DL(Op);
1615+
unsigned Opcode;
1616+
switch (Op.getOpcode()) {
1617+
case ISD::UMUL_LOHI:
1618+
Opcode = WebAssemblyISD::I64_MUL_WIDE_U;
1619+
break;
1620+
case ISD::SMUL_LOHI:
1621+
Opcode = WebAssemblyISD::I64_MUL_WIDE_S;
1622+
break;
1623+
default:
1624+
llvm_unreachable("unexpected opcode");
1625+
}
1626+
SDValue LHS = Op.getOperand(0);
1627+
SDValue RHS = Op.getOperand(1);
1628+
SDValue Hi =
1629+
DAG.getNode(Opcode, DL, DAG.getVTList(MVT::i64, MVT::i64), LHS, RHS);
1630+
SDValue Lo(Hi.getNode(), 1);
1631+
SDValue Ops[] = {Hi, Lo};
1632+
return DAG.getMergeValues(Ops, DL);
1633+
}
1634+
1635+
SDValue WebAssemblyTargetLowering::Replace128Op(SDNode *N,
1636+
SelectionDAG &DAG) const {
1637+
assert(Subtarget->hasWideArithmetic());
1638+
auto ValTy = N->getValueType(0);
1639+
assert(ValTy == MVT::i128);
1640+
SDLoc DL(N);
1641+
unsigned Opcode;
1642+
switch (N->getOpcode()) {
1643+
case ISD::ADD:
1644+
Opcode = WebAssemblyISD::I64_ADD128;
1645+
break;
1646+
case ISD::SUB:
1647+
Opcode = WebAssemblyISD::I64_SUB128;
1648+
break;
1649+
default:
1650+
llvm_unreachable("unexpected opcode");
1651+
}
1652+
SDValue LHS = N->getOperand(0);
1653+
SDValue RHS = N->getOperand(1);
1654+
1655+
SDValue C0 = DAG.getConstant(0, DL, MVT::i64);
1656+
SDValue C1 = DAG.getConstant(1, DL, MVT::i64);
1657+
SDValue LHS_0 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i64, LHS, C0);
1658+
SDValue LHS_1 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i64, LHS, C1);
1659+
SDValue RHS_0 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i64, RHS, C0);
1660+
SDValue RHS_1 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i64, RHS, C1);
1661+
SDValue Result_LO = DAG.getNode(Opcode, DL, DAG.getVTList(MVT::i64, MVT::i64),
1662+
LHS_0, LHS_1, RHS_0, RHS_1);
1663+
SDValue Result_HI(Result_LO.getNode(), 1);
1664+
return DAG.getNode(ISD::BUILD_PAIR, DL, N->getVTList(), Result_LO, Result_HI);
1665+
}
1666+
15961667
SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op,
15971668
SelectionDAG &DAG) const {
15981669
SDValue Src = Op.getOperand(2);

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ class WebAssemblyTargetLowering final : public TargetLowering {
136136
SDValue LowerFP_TO_INT_SAT(SDValue Op, SelectionDAG &DAG) const;
137137
SDValue LowerLoad(SDValue Op, SelectionDAG &DAG) const;
138138
SDValue LowerStore(SDValue Op, SelectionDAG &DAG) const;
139+
SDValue LowerMUL_LOHI(SDValue Op, SelectionDAG &DAG) const;
140+
SDValue Replace128Op(SDNode *N, SelectionDAG &DAG) const;
139141

140142
// Custom DAG combine hooks
141143
SDValue

llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ def HasTailCall :
8484
Predicate<"Subtarget->hasTailCall()">,
8585
AssemblerPredicate<(all_of FeatureTailCall), "tail-call">;
8686

87+
def HasWideArithmetic :
88+
Predicate<"Subtarget->hasWideArithmetic()">,
89+
AssemblerPredicate<(all_of FeatureWideArithmetic), "wide-arithmetic">;
90+
8791
//===----------------------------------------------------------------------===//
8892
// WebAssembly-specific DAG Node Types.
8993
//===----------------------------------------------------------------------===//

llvm/lib/Target/WebAssembly/WebAssemblyInstrInteger.td

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,48 @@ def : Pat<(select (i32 (seteq I32:$cond, 0)), I32:$lhs, I32:$rhs),
129129
(SELECT_I32 I32:$rhs, I32:$lhs, I32:$cond)>;
130130
def : Pat<(select (i32 (seteq I32:$cond, 0)), I64:$lhs, I64:$rhs),
131131
(SELECT_I64 I64:$rhs, I64:$lhs, I32:$cond)>;
132+
133+
let Predicates = [HasWideArithmetic] in {
134+
defm I64_ADD128 : I<(outs I64:$lo, I64:$hi), (ins I64:$lhs_lo, I64:$lhs_hi, I64:$rhs_lo, I64:$rhs_hi),
135+
(outs), (ins),
136+
[],
137+
"i64.add128\t$lo, $hi, $lhs_lo, $lhs_hi, $rhs_lo, $rhs_hi",
138+
"i64.add128",
139+
0xfc13>;
140+
defm I64_SUB128 : I<(outs I64:$lo, I64:$hi), (ins I64:$lhs_lo, I64:$lhs_hi, I64:$rhs_lo, I64:$rhs_hi),
141+
(outs), (ins),
142+
[],
143+
"i64.sub128\t$lo, $hi, $lhs_lo, $lhs_hi, $rhs_lo, $rhs_hi",
144+
"i64.sub128",
145+
0xfc14>;
146+
defm I64_MUL_WIDE_S : I<(outs I64:$lo, I64:$hi), (ins I64:$lhs, I64:$rhs),
147+
(outs), (ins),
148+
[],
149+
"i64.mul_wide_s\t$lo, $hi, $lhs, $rhs",
150+
"i64.mul_wide_s",
151+
0xfc15>;
152+
defm I64_MUL_WIDE_U : I<(outs I64:$lo, I64:$hi), (ins I64:$lhs, I64:$rhs),
153+
(outs), (ins),
154+
[],
155+
"i64.mul_wide_u\t$lo, $hi, $lhs, $rhs",
156+
"i64.mul_wide_u",
157+
0xfc16>;
158+
} // Predicates = [HasWideArithmetic]
159+
160+
def wasm_binop128_t : SDTypeProfile<2, 4, []>;
161+
def wasm_add128 : SDNode<"WebAssemblyISD::I64_ADD128", wasm_binop128_t>;
162+
def wasm_sub128 : SDNode<"WebAssemblyISD::I64_SUB128", wasm_binop128_t>;
163+
164+
def : Pat<(wasm_add128 I64:$a, I64:$b, I64:$c, I64:$d),
165+
(I64_ADD128 $a, $b, $c, $d)>;
166+
def : Pat<(wasm_sub128 I64:$a, I64:$b, I64:$c, I64:$d),
167+
(I64_SUB128 $a, $b, $c, $d)>;
168+
169+
def wasm_mul_wide_t : SDTypeProfile<2, 2, []>;
170+
def wasm_mul_wide_s : SDNode<"WebAssemblyISD::I64_MUL_WIDE_S", wasm_mul_wide_t>;
171+
def wasm_mul_wide_u : SDNode<"WebAssemblyISD::I64_MUL_WIDE_U", wasm_mul_wide_t>;
172+
173+
def : Pat<(wasm_mul_wide_s I64:$x, I64:$y),
174+
(I64_MUL_WIDE_S $x, $y)>;
175+
def : Pat<(wasm_mul_wide_u I64:$x, I64:$y),
176+
(I64_MUL_WIDE_U $x, $y)>;

llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
5151
bool HasReferenceTypes = false;
5252
bool HasSignExt = false;
5353
bool HasTailCall = false;
54+
bool HasWideArithmetic = false;
5455

5556
/// What processor and OS we're targeting.
5657
Triple TargetTriple;
@@ -106,6 +107,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
106107
bool hasSignExt() const { return HasSignExt; }
107108
bool hasSIMD128() const { return SIMDLevel >= SIMD128; }
108109
bool hasTailCall() const { return HasTailCall; }
110+
bool hasWideArithmetic() const { return HasWideArithmetic; }
109111

110112
/// Parses features string setting specified subtarget options. Definition of
111113
/// function is auto generated by tblgen.

0 commit comments

Comments
 (0)