Skip to content

Commit 38d18d9

Browse files
committed
[SVE] Add support to vectorize_width loop pragma for scalable vectors
This patch adds support for two new variants of the vectorize_width pragma: 1. vectorize_width(X[, fixed|scalable]) where an optional second parameter is passed to the vectorize_width pragma, which indicates if the user wishes to use fixed width or scalable vectorization. For example the user can now write something like: #pragma clang loop vectorize_width(4, fixed) or #pragma clang loop vectorize_width(4, scalable) In the absence of a second parameter it is assumed the user wants fixed width vectorization, in order to maintain compatibility with existing code. 2. vectorize_width(fixed|scalable) where the width is left unspecified, but the user hints what type of vectorization they prefer, either fixed width or scalable. I have implemented this by making use of the LLVM loop hint attribute: llvm.loop.vectorize.scalable.enable Tests were added to clang/test/CodeGenCXX/pragma-loop.cpp for both the 'fixed' and 'scalable' optional parameter. See this thread for context: http://lists.llvm.org/pipermail/cfe-dev/2020-November/067262.html Differential Revision: https://reviews.llvm.org/D89031
1 parent 1e7efd3 commit 38d18d9

File tree

12 files changed

+282
-40
lines changed

12 files changed

+282
-40
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3107,8 +3107,18 @@ manually enable vectorization or interleaving.
31073107
...
31083108
}
31093109
3110-
The vector width is specified by ``vectorize_width(_value_)`` and the interleave
3111-
count is specified by ``interleave_count(_value_)``, where
3110+
The vector width is specified by
3111+
``vectorize_width(_value_[, fixed|scalable])``, where _value_ is a positive
3112+
integer and the type of vectorization can be specified with an optional
3113+
second parameter. The default for the second parameter is 'fixed' and
3114+
refers to fixed width vectorization, whereas 'scalable' indicates the
3115+
compiler should use scalable vectors instead. Another use of vectorize_width
3116+
is ``vectorize_width(fixed|scalable)`` where the user can hint at the type
3117+
of vectorization to use without specifying the exact width. In both variants
3118+
of the pragma the vectorizer may decide to fall back on fixed width
3119+
vectorization if the target does not support scalable vectors.
3120+
3121+
The interleave count is specified by ``interleave_count(_value_)``, where
31123122
_value_ is a positive integer. This is useful for specifying the optimal
31133123
width/count of the set of target architectures supported by your application.
31143124

clang/include/clang/Basic/Attr.td

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3375,8 +3375,10 @@ def LoopHint : Attr {
33753375
"PipelineDisabled", "PipelineInitiationInterval", "Distribute",
33763376
"VectorizePredicate"]>,
33773377
EnumArgument<"State", "LoopHintState",
3378-
["enable", "disable", "numeric", "assume_safety", "full"],
3379-
["Enable", "Disable", "Numeric", "AssumeSafety", "Full"]>,
3378+
["enable", "disable", "numeric", "fixed_width",
3379+
"scalable_width", "assume_safety", "full"],
3380+
["Enable", "Disable", "Numeric", "FixedWidth",
3381+
"ScalableWidth", "AssumeSafety", "Full"]>,
33803382
ExprArgument<"Value">];
33813383

33823384
let AdditionalMembers = [{

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,12 @@ def err_pragma_loop_invalid_option : Error<
13961396
"%select{invalid|missing}0 option%select{ %1|}0; expected vectorize, "
13971397
"vectorize_width, interleave, interleave_count, unroll, unroll_count, "
13981398
"pipeline, pipeline_initiation_interval, vectorize_predicate, or distribute">;
1399+
def err_pragma_loop_invalid_vectorize_option : Error<
1400+
"vectorize_width loop hint malformed; use vectorize_width(X, fixed) or "
1401+
"vectorize_width(X, scalable) where X is an integer, or vectorize_width('fixed' or 'scalable')">;
1402+
def note_pragma_loop_invalid_vectorize_option : Note<
1403+
"vectorize_width loop hint malformed; use vectorize_width(X, fixed) or "
1404+
"vectorize_width(X, scalable) where X is an integer, or vectorize_width('fixed' or 'scalable')">;
13991405

14001406
def err_pragma_fp_invalid_option : Error<
14011407
"%select{invalid|missing}0 option%select{ %1|}0; expected 'contract', 'reassociate' or 'exceptions'">;

clang/lib/AST/AttrImpl.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,16 @@ std::string LoopHintAttr::getValueString(const PrintingPolicy &Policy) const {
4242
OS << "(";
4343
if (state == Numeric)
4444
value->printPretty(OS, nullptr, Policy);
45-
else if (state == Enable)
45+
else if (state == FixedWidth || state == ScalableWidth) {
46+
if (value) {
47+
value->printPretty(OS, nullptr, Policy);
48+
if (state == ScalableWidth)
49+
OS << ", scalable";
50+
} else if (state == ScalableWidth)
51+
OS << "scalable";
52+
else
53+
OS << "fixed";
54+
} else if (state == Enable)
4655
OS << "enable";
4756
else if (state == Full)
4857
OS << "full";

clang/lib/CodeGen/CGLoopInfo.cpp

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,8 @@ LoopInfo::createLoopVectorizeMetadata(const LoopAttributes &Attrs,
217217
Enabled = false;
218218
else if (Attrs.VectorizeEnable != LoopAttributes::Unspecified ||
219219
Attrs.VectorizePredicateEnable != LoopAttributes::Unspecified ||
220-
Attrs.InterleaveCount != 0 || Attrs.VectorizeWidth != 0)
220+
Attrs.InterleaveCount != 0 || Attrs.VectorizeWidth != 0 ||
221+
Attrs.VectorizeScalable != LoopAttributes::Unspecified)
221222
Enabled = true;
222223

223224
if (Enabled != true) {
@@ -271,6 +272,16 @@ LoopInfo::createLoopVectorizeMetadata(const LoopAttributes &Attrs,
271272
MDString::get(Ctx, "llvm.loop.vectorize.width"),
272273
ConstantAsMetadata::get(ConstantInt::get(llvm::Type::getInt32Ty(Ctx),
273274
Attrs.VectorizeWidth))};
275+
276+
Args.push_back(MDNode::get(Ctx, Vals));
277+
}
278+
279+
if (Attrs.VectorizeScalable != LoopAttributes::Unspecified) {
280+
bool IsScalable = Attrs.VectorizeScalable == LoopAttributes::Enable;
281+
Metadata *Vals[] = {
282+
MDString::get(Ctx, "llvm.loop.vectorize.scalable.enable"),
283+
ConstantAsMetadata::get(
284+
ConstantInt::get(llvm::Type::getInt1Ty(Ctx), IsScalable))};
274285
Args.push_back(MDNode::get(Ctx, Vals));
275286
}
276287

@@ -286,10 +297,16 @@ LoopInfo::createLoopVectorizeMetadata(const LoopAttributes &Attrs,
286297
// vectorize.enable is set if:
287298
// 1) loop hint vectorize.enable is set, or
288299
// 2) it is implied when vectorize.predicate is set, or
289-
// 3) it is implied when vectorize.width is set.
300+
// 3) it is implied when vectorize.width is set to a value > 1
301+
// 4) it is implied when vectorize.scalable.enable is true
302+
// 5) it is implied when vectorize.width is unset (0) and the user
303+
// explicitly requested fixed-width vectorization, i.e.
304+
// vectorize.scalable.enable is false.
290305
if (Attrs.VectorizeEnable != LoopAttributes::Unspecified ||
291-
IsVectorPredicateEnabled ||
292-
Attrs.VectorizeWidth > 1 ) {
306+
IsVectorPredicateEnabled || Attrs.VectorizeWidth > 1 ||
307+
Attrs.VectorizeScalable == LoopAttributes::Enable ||
308+
(Attrs.VectorizeScalable == LoopAttributes::Disable &&
309+
Attrs.VectorizeWidth != 1)) {
293310
bool AttrVal = Attrs.VectorizeEnable != LoopAttributes::Disable;
294311
Args.push_back(
295312
MDNode::get(Ctx, {MDString::get(Ctx, "llvm.loop.vectorize.enable"),
@@ -433,13 +450,15 @@ LoopAttributes::LoopAttributes(bool IsParallel)
433450
UnrollEnable(LoopAttributes::Unspecified),
434451
UnrollAndJamEnable(LoopAttributes::Unspecified),
435452
VectorizePredicateEnable(LoopAttributes::Unspecified), VectorizeWidth(0),
436-
InterleaveCount(0), UnrollCount(0), UnrollAndJamCount(0),
453+
VectorizeScalable(LoopAttributes::Unspecified), InterleaveCount(0),
454+
UnrollCount(0), UnrollAndJamCount(0),
437455
DistributeEnable(LoopAttributes::Unspecified), PipelineDisabled(false),
438456
PipelineInitiationInterval(0), MustProgress(false) {}
439457

440458
void LoopAttributes::clear() {
441459
IsParallel = false;
442460
VectorizeWidth = 0;
461+
VectorizeScalable = LoopAttributes::Unspecified;
443462
InterleaveCount = 0;
444463
UnrollCount = 0;
445464
UnrollAndJamCount = 0;
@@ -466,6 +485,7 @@ LoopInfo::LoopInfo(BasicBlock *Header, const LoopAttributes &Attrs,
466485
}
467486

468487
if (!Attrs.IsParallel && Attrs.VectorizeWidth == 0 &&
488+
Attrs.VectorizeScalable == LoopAttributes::Unspecified &&
469489
Attrs.InterleaveCount == 0 && Attrs.UnrollCount == 0 &&
470490
Attrs.UnrollAndJamCount == 0 && !Attrs.PipelineDisabled &&
471491
Attrs.PipelineInitiationInterval == 0 &&
@@ -501,6 +521,7 @@ void LoopInfo::finish() {
501521
BeforeJam.IsParallel = AfterJam.IsParallel = Attrs.IsParallel;
502522

503523
BeforeJam.VectorizeWidth = Attrs.VectorizeWidth;
524+
BeforeJam.VectorizeScalable = Attrs.VectorizeScalable;
504525
BeforeJam.InterleaveCount = Attrs.InterleaveCount;
505526
BeforeJam.VectorizeEnable = Attrs.VectorizeEnable;
506527
BeforeJam.DistributeEnable = Attrs.DistributeEnable;
@@ -543,7 +564,8 @@ void LoopInfo::finish() {
543564
SmallVector<Metadata *, 1> BeforeLoopProperties;
544565
if (BeforeJam.VectorizeEnable != LoopAttributes::Unspecified ||
545566
BeforeJam.VectorizePredicateEnable != LoopAttributes::Unspecified ||
546-
BeforeJam.InterleaveCount != 0 || BeforeJam.VectorizeWidth != 0)
567+
BeforeJam.InterleaveCount != 0 || BeforeJam.VectorizeWidth != 0 ||
568+
BeforeJam.VectorizeScalable == LoopAttributes::Enable)
547569
BeforeLoopProperties.push_back(
548570
MDNode::get(Ctx, MDString::get(Ctx, "llvm.loop.isvectorized")));
549571

@@ -620,6 +642,7 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
620642
case LoopHintAttr::Vectorize:
621643
// Disable vectorization by specifying a width of 1.
622644
setVectorizeWidth(1);
645+
setVectorizeScalable(LoopAttributes::Unspecified);
623646
break;
624647
case LoopHintAttr::Interleave:
625648
// Disable interleaving by speciyfing a count of 1.
@@ -721,11 +744,23 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
721744
break;
722745
}
723746
break;
724-
case LoopHintAttr::Numeric:
747+
case LoopHintAttr::FixedWidth:
748+
case LoopHintAttr::ScalableWidth:
725749
switch (Option) {
726750
case LoopHintAttr::VectorizeWidth:
727-
setVectorizeWidth(ValueInt);
751+
setVectorizeScalable(State == LoopHintAttr::ScalableWidth
752+
? LoopAttributes::Enable
753+
: LoopAttributes::Disable);
754+
if (LH->getValue())
755+
setVectorizeWidth(ValueInt);
728756
break;
757+
default:
758+
llvm_unreachable("Options cannot be used with 'scalable' hint.");
759+
break;
760+
}
761+
break;
762+
case LoopHintAttr::Numeric:
763+
switch (Option) {
729764
case LoopHintAttr::InterleaveCount:
730765
setInterleaveCount(ValueInt);
731766
break;
@@ -742,6 +777,7 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
742777
case LoopHintAttr::UnrollAndJam:
743778
case LoopHintAttr::VectorizePredicate:
744779
case LoopHintAttr::Vectorize:
780+
case LoopHintAttr::VectorizeWidth:
745781
case LoopHintAttr::Interleave:
746782
case LoopHintAttr::Distribute:
747783
case LoopHintAttr::PipelineDisabled:

clang/lib/CodeGen/CGLoopInfo.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ struct LoopAttributes {
5858
/// Value for llvm.loop.vectorize.width metadata.
5959
unsigned VectorizeWidth;
6060

61+
// Value for llvm.loop.vectorize.scalable.enable
62+
LVEnableState VectorizeScalable;
63+
6164
/// Value for llvm.loop.interleave.count metadata.
6265
unsigned InterleaveCount;
6366

@@ -258,6 +261,10 @@ class LoopInfoStack {
258261
/// Set the vectorize width for the next loop pushed.
259262
void setVectorizeWidth(unsigned W) { StagedAttrs.VectorizeWidth = W; }
260263

264+
void setVectorizeScalable(const LoopAttributes::LVEnableState &State) {
265+
StagedAttrs.VectorizeScalable = State;
266+
}
267+
261268
/// Set the interleave count for the next loop pushed.
262269
void setInterleaveCount(unsigned C) { StagedAttrs.InterleaveCount = C; }
263270

clang/lib/Parse/ParsePragma.cpp

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1187,12 +1187,79 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) {
11871187
Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
11881188
<< PragmaLoopHintString(Info->PragmaName, Info->Option);
11891189
Hint.StateLoc = IdentifierLoc::create(Actions.Context, StateLoc, StateInfo);
1190+
} else if (OptionInfo && OptionInfo->getName() == "vectorize_width") {
1191+
PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/false,
1192+
/*IsReinject=*/false);
1193+
ConsumeAnnotationToken();
1194+
1195+
SourceLocation StateLoc = Toks[0].getLocation();
1196+
IdentifierInfo *StateInfo = Toks[0].getIdentifierInfo();
1197+
StringRef IsScalableStr = StateInfo ? StateInfo->getName() : "";
1198+
1199+
// Look for vectorize_width(fixed|scalable)
1200+
if (IsScalableStr == "scalable" || IsScalableStr == "fixed") {
1201+
PP.Lex(Tok); // Identifier
1202+
1203+
if (Toks.size() > 2) {
1204+
Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
1205+
<< PragmaLoopHintString(Info->PragmaName, Info->Option);
1206+
while (Tok.isNot(tok::eof))
1207+
ConsumeAnyToken();
1208+
}
1209+
1210+
Hint.StateLoc =
1211+
IdentifierLoc::create(Actions.Context, StateLoc, StateInfo);
1212+
1213+
ConsumeToken(); // Consume the constant expression eof terminator.
1214+
} else {
1215+
// Enter constant expression including eof terminator into token stream.
1216+
ExprResult R = ParseConstantExpression();
1217+
1218+
if (R.isInvalid() && !Tok.is(tok::comma))
1219+
Diag(Toks[0].getLocation(),
1220+
diag::note_pragma_loop_invalid_vectorize_option);
1221+
1222+
bool Arg2Error = false;
1223+
if (Tok.is(tok::comma)) {
1224+
PP.Lex(Tok); // ,
1225+
1226+
StateInfo = Tok.getIdentifierInfo();
1227+
IsScalableStr = StateInfo->getName();
1228+
1229+
if (IsScalableStr != "scalable" && IsScalableStr != "fixed") {
1230+
Diag(Tok.getLocation(),
1231+
diag::err_pragma_loop_invalid_vectorize_option);
1232+
Arg2Error = true;
1233+
} else
1234+
Hint.StateLoc =
1235+
IdentifierLoc::create(Actions.Context, StateLoc, StateInfo);
1236+
1237+
PP.Lex(Tok); // Identifier
1238+
}
1239+
1240+
// Tokens following an error in an ill-formed constant expression will
1241+
// remain in the token stream and must be removed.
1242+
if (Tok.isNot(tok::eof)) {
1243+
Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
1244+
<< PragmaLoopHintString(Info->PragmaName, Info->Option);
1245+
while (Tok.isNot(tok::eof))
1246+
ConsumeAnyToken();
1247+
}
1248+
1249+
ConsumeToken(); // Consume the constant expression eof terminator.
1250+
1251+
if (Arg2Error || R.isInvalid() ||
1252+
Actions.CheckLoopHintExpr(R.get(), Toks[0].getLocation()))
1253+
return false;
1254+
1255+
// Argument is a constant expression with an integer type.
1256+
Hint.ValueExpr = R.get();
1257+
}
11901258
} else {
11911259
// Enter constant expression including eof terminator into token stream.
11921260
PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/false,
11931261
/*IsReinject=*/false);
11941262
ConsumeAnnotationToken();
1195-
11961263
ExprResult R = ParseConstantExpression();
11971264

11981265
// Tokens following an error in an ill-formed constant expression will

clang/lib/Sema/SemaStmtAttr.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "clang/AST/EvaluatedExprVisitor.h"
14-
#include "clang/Sema/SemaInternal.h"
1513
#include "clang/AST/ASTContext.h"
14+
#include "clang/AST/EvaluatedExprVisitor.h"
1615
#include "clang/Basic/SourceManager.h"
16+
#include "clang/Basic/TargetInfo.h"
1717
#include "clang/Sema/DelayedDiagnostic.h"
1818
#include "clang/Sema/Lookup.h"
1919
#include "clang/Sema/ScopeInfo.h"
20+
#include "clang/Sema/SemaInternal.h"
2021
#include "llvm/ADT/StringExtras.h"
2122

2223
using namespace clang;
@@ -139,10 +140,18 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
139140
LoopHintAttr::PipelineInitiationInterval)
140141
.Case("distribute", LoopHintAttr::Distribute)
141142
.Default(LoopHintAttr::Vectorize);
142-
if (Option == LoopHintAttr::VectorizeWidth ||
143-
Option == LoopHintAttr::InterleaveCount ||
144-
Option == LoopHintAttr::UnrollCount ||
145-
Option == LoopHintAttr::PipelineInitiationInterval) {
143+
if (Option == LoopHintAttr::VectorizeWidth) {
144+
assert((ValueExpr || (StateLoc && StateLoc->Ident)) &&
145+
"Attribute must have a valid value expression or argument.");
146+
if (ValueExpr && S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc()))
147+
return nullptr;
148+
if (StateLoc && StateLoc->Ident && StateLoc->Ident->isStr("scalable"))
149+
State = LoopHintAttr::ScalableWidth;
150+
else
151+
State = LoopHintAttr::FixedWidth;
152+
} else if (Option == LoopHintAttr::InterleaveCount ||
153+
Option == LoopHintAttr::UnrollCount ||
154+
Option == LoopHintAttr::PipelineInitiationInterval) {
146155
assert(ValueExpr && "Attribute must have a valid value expression.");
147156
if (S.CheckLoopHintExpr(ValueExpr, St->getBeginLoc()))
148157
return nullptr;

clang/test/AST/ast-print-pragmas.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,36 @@ void test(int *List, int Length) {
88
int i = 0;
99
#pragma clang loop vectorize_width(4)
1010
#pragma clang loop interleave_count(8)
11+
// CHECK-NEXT: while (i < Length)
12+
while (i < Length) {
13+
List[i] = i * 2;
14+
i++;
15+
}
16+
i = 0;
17+
18+
// CHECK: #pragma clang loop vectorize_width(4, scalable)
19+
20+
#pragma clang loop vectorize_width(4, scalable)
21+
// CHECK-NEXT: while (i < Length)
22+
while (i < Length) {
23+
List[i] = i * 2;
24+
i++;
25+
}
26+
i = 0;
27+
28+
// CHECK: #pragma clang loop vectorize_width(fixed)
29+
30+
#pragma clang loop vectorize_width(fixed)
31+
// CHECK-NEXT: while (i < Length)
32+
while (i < Length) {
33+
List[i] = i * 2;
34+
i++;
35+
}
36+
i = 0;
37+
38+
// CHECK: #pragma clang loop vectorize_width(scalable)
39+
40+
#pragma clang loop vectorize_width(scalable)
1141
// CHECK-NEXT: while (i < Length)
1242
while (i < Length) {
1343
List[i] = i * 2;

clang/test/CodeGenCXX/pragma-loop-pr27643.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,14 @@ void loop4(int *List, int Length) {
4040
List[i] = i * 2;
4141
}
4242

43-
// CHECK: ![[LOOP1]] = distinct !{![[LOOP1]], [[MP:![0-9]+]], ![[VEC_WIDTH_1:.*]], ![[VEC_ENABLE:.*]]}
43+
// CHECK: ![[LOOP1]] = distinct !{![[LOOP1]], [[MP:![0-9]+]], ![[VEC_WIDTH_1:.*]], ![[FIXED_WIDTH:.*]], ![[VEC_ENABLE:.*]]}
4444
// CHECK: ![[VEC_WIDTH_1]] = !{!"llvm.loop.vectorize.width", i32 1}
45+
// CHECK: ![[FIXED_WIDTH]] = !{!"llvm.loop.vectorize.scalable.enable", i1 false}
4546
// CHECK: ![[VEC_ENABLE]] = !{!"llvm.loop.vectorize.enable", i1 true}
4647

47-
// CHECK: ![[LOOP2]] = distinct !{![[LOOP2]], [[MP]], ![[VEC_WIDTH_2:.*]], ![[VEC_ENABLE]]}
48+
// CHECK: ![[LOOP2]] = distinct !{![[LOOP2]], [[MP]], ![[VEC_WIDTH_2:.*]], ![[FIXED_WIDTH:.*]], ![[VEC_ENABLE]]}
4849
// CHECK: ![[VEC_WIDTH_2]] = !{!"llvm.loop.vectorize.width", i32 2}
4950

50-
// CHECK: ![[LOOP3]] = distinct !{![[LOOP3]], [[MP]], ![[VEC_WIDTH_1]]}
51+
// CHECK: ![[LOOP3]] = distinct !{![[LOOP3]], [[MP]], ![[VEC_WIDTH_1]], ![[FIXED_WIDTH]]}
5152

52-
// CHECK: ![[LOOP4]] = distinct !{![[LOOP4]], [[MP]], ![[VEC_WIDTH_2]], ![[VEC_ENABLE]]}
53+
// CHECK: ![[LOOP4]] = distinct !{![[LOOP4]], [[MP]], ![[VEC_WIDTH_2]], ![[FIXED_WIDTH]], ![[VEC_ENABLE]]}

0 commit comments

Comments
 (0)