Skip to content

Commit e97f13e

Browse files
authored
[Compilation Hints] Start Compilation Hints by adding Call support in the text format (#7587)
Left as TODOs are other instructions, the binary format, and function-level annotations.
1 parent d7aaeab commit e97f13e

File tree

9 files changed

+156
-23
lines changed

9 files changed

+156
-23
lines changed

src/parser/contexts.h

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,9 +1257,47 @@ struct ParseImplicitTypeDefsCtx : TypeParserCtx<ParseImplicitTypeDefsCtx> {
12571257
}
12581258
};
12591259

1260+
struct AnnotationParserCtx {
1261+
// Return the inline hint for a call instruction, if there is one.
1262+
std::optional<std::uint8_t>
1263+
getInlineHint(const std::vector<Annotation>& annotations) {
1264+
// Find and apply (the last) inline hint.
1265+
const Annotation* hint = nullptr;
1266+
for (auto& a : annotations) {
1267+
if (a.kind == Annotations::InlineHint) {
1268+
hint = &a;
1269+
}
1270+
}
1271+
if (!hint) {
1272+
return std::nullopt;
1273+
}
1274+
1275+
Lexer lexer(hint->contents);
1276+
if (lexer.empty()) {
1277+
std::cerr << "warning: empty InlineHint\n";
1278+
return std::nullopt;
1279+
}
1280+
1281+
auto str = lexer.takeString();
1282+
if (!str || str->size() != 1) {
1283+
std::cerr << "warning: invalid InlineHint string\n";
1284+
return std::nullopt;
1285+
}
1286+
1287+
uint8_t value = (*str)[0];
1288+
if (value > 127) {
1289+
std::cerr << "warning: invalid InlineHint value\n";
1290+
return std::nullopt;
1291+
}
1292+
1293+
return value;
1294+
}
1295+
};
1296+
12601297
// Phase 4: Parse and set the types of module elements.
12611298
struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
1262-
NullInstrParserCtx {
1299+
NullInstrParserCtx,
1300+
AnnotationParserCtx {
12631301
// In this phase we have constructed all the types, so we can materialize and
12641302
// validate them when they are used.
12651303

@@ -1367,6 +1405,13 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
13671405
Builder::addVar(f.get(), l.name, l.type);
13681406
}
13691407
}
1408+
// TODO: Add function-level annotations (stored using the nullptr key, as
1409+
// they are tied to no instruction in particular), but this should wait on
1410+
// figuring out
1411+
// https://github.com/WebAssembly/tool-conventions/issues/251
1412+
// if (auto inline_ = getInlineHint(annotations)) {
1413+
// f->codeAnnotations[nullptr].inline_ = inline_;
1414+
// }
13701415
return Ok{};
13711416
}
13721417

@@ -1427,7 +1472,7 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
14271472
};
14281473

14291474
// Phase 5: Parse module element definitions, including instructions.
1430-
struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
1475+
struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx>, AnnotationParserCtx {
14311476
using GlobalTypeT = Ok;
14321477
using TableTypeT = Ok;
14331478
using TypeUseT = HeapType;
@@ -2329,7 +2374,8 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
23292374
const std::vector<Annotation>& annotations,
23302375
Name func,
23312376
bool isReturn) {
2332-
return withLoc(pos, irBuilder.makeCall(func, isReturn));
2377+
auto inline_ = getInlineHint(annotations);
2378+
return withLoc(pos, irBuilder.makeCall(func, isReturn, inline_));
23332379
}
23342380

23352381
Result<> makeCallIndirect(Index pos,

src/passes/Print.cpp

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
//
2020

2121
#include <algorithm>
22+
#include <fstream>
2223

2324
#include <ir/iteration.h>
2425
#include <ir/module-utils.h>
@@ -275,6 +276,10 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
275276
// Prints debug info and code annotations.
276277
void printMetadata(Expression* curr);
277278

279+
// Print code annotations for an expression. If the expression is nullptr,
280+
// prints for the current function.
281+
void printCodeAnnotations(Expression* curr);
282+
278283
void printExpressionContents(Expression* curr);
279284

280285
void visit(Expression* curr) {
@@ -2642,18 +2647,8 @@ void PrintSExpression::printMetadata(Expression* curr) {
26422647
}
26432648
}
26442649

2645-
// Show a code annotation, if there is one.
2646-
if (auto iter = currFunction->codeAnnotations.find(curr);
2647-
iter != currFunction->codeAnnotations.end()) {
2648-
auto& annotation = iter->second;
2649-
if (annotation.branchLikely) {
2650-
Colors::grey(o);
2651-
o << "(@" << Annotations::BranchHint << " \"\\0"
2652-
<< (*annotation.branchLikely ? "1" : "0") << "\")\n";
2653-
restoreNormalColor(o);
2654-
doIndent(o, indent);
2655-
}
2656-
}
2650+
// Show code annotations.
2651+
printCodeAnnotations(curr);
26572652
}
26582653
}
26592654

@@ -2670,6 +2665,31 @@ void PrintSExpression::printDebugDelimiterLocation(Expression* curr, Index i) {
26702665
}
26712666
}
26722667

2668+
void PrintSExpression::printCodeAnnotations(Expression* curr) {
2669+
if (auto iter = currFunction->codeAnnotations.find(curr);
2670+
iter != currFunction->codeAnnotations.end()) {
2671+
auto& annotation = iter->second;
2672+
if (annotation.branchLikely) {
2673+
Colors::grey(o);
2674+
o << "(@" << Annotations::BranchHint << " \"\\0"
2675+
<< (*annotation.branchLikely ? "1" : "0") << "\")\n";
2676+
restoreNormalColor(o);
2677+
doIndent(o, indent);
2678+
}
2679+
if (annotation.inline_) {
2680+
Colors::grey(o);
2681+
std::ofstream saved;
2682+
saved.copyfmt(std::cout);
2683+
o << "(@" << Annotations::InlineHint << " \"\\" << std::hex
2684+
<< std::setfill('0') << std::setw(2) << int(*annotation.inline_)
2685+
<< "\")\n";
2686+
std::cout.copyfmt(saved);
2687+
restoreNormalColor(o);
2688+
doIndent(o, indent);
2689+
}
2690+
}
2691+
}
2692+
26732693
void PrintSExpression::printExpressionContents(Expression* curr) {
26742694
PrintExpressionContents(*this).visit(curr);
26752695
}
@@ -3125,6 +3145,9 @@ void PrintSExpression::visitDefinedFunction(Function* curr) {
31253145
if (currFunction->prologLocation) {
31263146
printDebugLocation(*currFunction->prologLocation);
31273147
}
3148+
// TODO: print code annotations in the right place, depending on
3149+
// https://github.com/WebAssembly/tool-conventions/issues/251
3150+
// printCodeAnnotations(nullptr);
31283151
handleSignature(curr, true);
31293152
incIndent();
31303153
for (size_t i = curr->getVarIndexBase(); i < curr->getNumLocals(); i++) {

src/wasm-annotations.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
namespace wasm::Annotations {
2727

2828
extern const Name BranchHint;
29+
extern const Name InlineHint;
2930

3031
} // namespace wasm::Annotations
3132

src/wasm-ir-builder.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
124124
std::optional<bool> likely = std::nullopt);
125125
Result<> makeSwitch(const std::vector<Index>& labels, Index defaultLabel);
126126
// Unlike Builder::makeCall, this assumes the function already exists.
127-
Result<> makeCall(Name func, bool isReturn);
127+
Result<> makeCall(Name func,
128+
bool isReturn,
129+
std::optional<std::uint8_t> inline_ = std::nullopt);
128130
Result<> makeCallIndirect(Name table, HeapType type, bool isReturn);
129131
Result<> makeLocalGet(Index local);
130132
Result<> makeLocalSet(Index local);
@@ -701,6 +703,9 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
701703
// Add a branch hint, if |likely| is present.
702704
void addBranchHint(Expression* expr, std::optional<bool> likely);
703705

706+
// Add an inlining hint, if |inline_| is present.
707+
void addInlineHint(Expression* expr, std::optional<std::uint8_t> inline_);
708+
704709
void dump();
705710
};
706711

src/wasm.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,14 +2191,21 @@ class Function : public Importable {
21912191
delimiterLocations;
21922192
BinaryLocations::FunctionLocations funcLocation;
21932193

2194-
// Code annotations. As with debug info, we do not store these on Expressions
2195-
// themselves, as we assume most instances are unannotated, and do not want to
2194+
// Code annotations for VMs. As with debug info, we do not store these on
2195+
// Expressions as we assume most instances are unannotated, and do not want to
21962196
// add constant memory overhead.
21972197
struct CodeAnnotation {
2198-
// Branch hinting proposal: Whether the branch is likely, or unlikely.
2198+
// Branch Hinting proposal: Whether the branch is likely, or unlikely.
21992199
std::optional<bool> branchLikely;
2200+
2201+
// Compilation Hints proposal.
2202+
static const uint8_t NeverInline = 0;
2203+
static const uint8_t AlwaysInline = 127;
2204+
std::optional<uint8_t> inline_;
22002205
};
22012206

2207+
// Function-level annotations are implemented with a key of nullptr, matching
2208+
// the 0 byte offset in the spec.
22022209
std::unordered_map<Expression*, CodeAnnotation> codeAnnotations;
22032210

22042211
// The effects for this function, if they have been computed. We use a shared
@@ -2208,8 +2215,8 @@ class Function : public Importable {
22082215
// See addsEffects() in pass.h for more details.
22092216
std::shared_ptr<EffectAnalyzer> effects;
22102217

2211-
// Inlining metadata: whether to disallow full and/or partial inlining (for
2212-
// details on what those mean, see Inlining.cpp).
2218+
// Inlining metadata: whether to disallow full and/or partial inlining. This
2219+
// is a toolchain-level hint. For more details, see Inlining.cpp.
22132220
bool noFullInline = false;
22142221
bool noPartialInline = false;
22152222

src/wasm/wasm-ir-builder.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,13 +1441,18 @@ Result<> IRBuilder::makeSwitch(const std::vector<Index>& labels,
14411441
return Ok{};
14421442
}
14431443

1444-
Result<> IRBuilder::makeCall(Name func, bool isReturn) {
1444+
Result<> IRBuilder::makeCall(Name func,
1445+
bool isReturn,
1446+
std::optional<std::uint8_t> inline_) {
14451447
auto sig = wasm.getFunction(func)->getSig();
14461448
Call curr(wasm.allocator);
14471449
curr.target = func;
14481450
curr.operands.resize(sig.params.size());
14491451
CHECK_ERR(visitCall(&curr));
1450-
push(builder.makeCall(curr.target, curr.operands, sig.results, isReturn));
1452+
auto* call =
1453+
builder.makeCall(curr.target, curr.operands, sig.results, isReturn);
1454+
push(call);
1455+
addInlineHint(call, inline_);
14511456
return Ok{};
14521457
}
14531458

@@ -2541,4 +2546,13 @@ void IRBuilder::addBranchHint(Expression* expr, std::optional<bool> likely) {
25412546
}
25422547
}
25432548

2549+
void IRBuilder::addInlineHint(Expression* expr,
2550+
std::optional<uint8_t> inline_) {
2551+
if (inline_) {
2552+
// Branches are only possible inside functions.
2553+
assert(func);
2554+
func->codeAnnotations[expr].inline_ = inline_;
2555+
}
2556+
}
2557+
25442558
} // namespace wasm

src/wasm/wasm.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ const char* CustomDescriptorsFeature = "custom-descriptors";
6767
namespace Annotations {
6868

6969
const Name BranchHint = "metadata.code.branch_hint";
70+
const Name InlineHint = "metadata.code.inline";
7071

7172
} // namespace Annotations
7273

File renamed without changes.

test/lit/compilation-hints.wast

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
2+
3+
;; RUN: wasm-opt -all %s -S -o - | filecheck %s
4+
5+
;; TODO: wasm-opt -all --roundtrip %s -S -o - | filecheck %s --check-prefix=RTRIP
6+
7+
(module
8+
;; CHECK: (type $0 (func))
9+
10+
;; CHECK: (func $func (type $0)
11+
;; CHECK-NEXT: (@metadata.code.inline "\00")
12+
;; CHECK-NEXT: (call $func)
13+
;; CHECK-NEXT: (@metadata.code.inline "\01")
14+
;; CHECK-NEXT: (call $func)
15+
;; CHECK-NEXT: (@metadata.code.inline "\7e")
16+
;; CHECK-NEXT: (call $func)
17+
;; CHECK-NEXT: (@metadata.code.inline "\7f")
18+
;; CHECK-NEXT: (call $func)
19+
;; CHECK-NEXT: (call $func)
20+
;; CHECK-NEXT: )
21+
(func $func
22+
(@metadata.code.inline "\00")
23+
(call $func)
24+
(@metadata.code.inline "\01")
25+
(call $func)
26+
(@metadata.code.inline "\7e")
27+
(call $func)
28+
(@metadata.code.inline "\7f")
29+
(call $func)
30+
;; Unannotated
31+
(call $func)
32+
)
33+
34+
;; TODO: test function annotations, after
35+
;; https://github.com/WebAssembly/tool-conventions/issues/251
36+
)

0 commit comments

Comments
 (0)