Skip to content

Commit 69577b2

Browse files
authored
[WebAssembly] Support type checker for new EH (#111069)
This adds supports for the new EH instructions (`try_table` and `throw_ref`) to the type checker. One thing I'd like to improve on is the locations in the errors for `catch_***` clauses. Currently they just point to the starting column of `try_table` instruction itself. But to figure out where catch clauses start you need to traverse `OperandVector` and check `WebAssemblyOperand::isCatchList` on them to see which one is the catch list operand, but `WebAssemblyOperand` class is in AsmParser and AsmTypeCheck does not have access to it: https://github.com/llvm/llvm-project/blob/cdfdc857cbab0418b7e5116fd4255eb5566588bd/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp#L43-L204 And even if AsmTypeCheck has access to it, currently it treats the list of catch clauses as a single `WebAssemblyOperand` so there is no way to get the starting location of each `catch_***` clause in the current structure. This also renames `valTypeToStackType` to `valTypesToStackTypes`, given that it takes two type lists.
1 parent 71b2c4d commit 69577b2

File tree

5 files changed

+113
-15
lines changed

5 files changed

+113
-15
lines changed

llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ void WebAssemblyAsmTypeCheck::localDecl(
5959
}
6060

6161
void WebAssemblyAsmTypeCheck::dumpTypeStack(Twine Msg) {
62-
LLVM_DEBUG({ dbgs() << Msg << getTypesString(Stack, 0) << "\n"; });
62+
LLVM_DEBUG({ dbgs() << Msg << getTypesString(Stack) << "\n"; });
6363
}
6464

6565
bool WebAssemblyAsmTypeCheck::typeError(SMLoc ErrorLoc, const Twine &Msg) {
@@ -116,8 +116,15 @@ std::string WebAssemblyAsmTypeCheck::getTypesString(ArrayRef<StackType> Types,
116116
return SS.str();
117117
}
118118

119+
std::string
120+
WebAssemblyAsmTypeCheck::getTypesString(ArrayRef<wasm::ValType> Types,
121+
size_t StartPos) {
122+
return getTypesString(valTypesToStackTypes(Types), StartPos);
123+
}
124+
119125
SmallVector<WebAssemblyAsmTypeCheck::StackType, 4>
120-
WebAssemblyAsmTypeCheck::valTypeToStackType(ArrayRef<wasm::ValType> ValTypes) {
126+
WebAssemblyAsmTypeCheck::valTypesToStackTypes(
127+
ArrayRef<wasm::ValType> ValTypes) {
121128
SmallVector<StackType, 4> Types(ValTypes.size());
122129
std::transform(ValTypes.begin(), ValTypes.end(), Types.begin(),
123130
[](wasm::ValType Val) -> StackType { return Val; });
@@ -127,7 +134,7 @@ WebAssemblyAsmTypeCheck::valTypeToStackType(ArrayRef<wasm::ValType> ValTypes) {
127134
bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
128135
ArrayRef<wasm::ValType> ValTypes,
129136
bool ExactMatch) {
130-
return checkTypes(ErrorLoc, valTypeToStackType(ValTypes), ExactMatch);
137+
return checkTypes(ErrorLoc, valTypesToStackTypes(ValTypes), ExactMatch);
131138
}
132139

133140
bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
@@ -178,14 +185,14 @@ bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
178185
: std::max((int)BlockStackStartPos,
179186
(int)Stack.size() - (int)Types.size());
180187
return typeError(ErrorLoc, "type mismatch, expected " +
181-
getTypesString(Types, 0) + " but got " +
188+
getTypesString(Types) + " but got " +
182189
getTypesString(Stack, StackStartPos));
183190
}
184191

185192
bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
186193
ArrayRef<wasm::ValType> ValTypes,
187194
bool ExactMatch) {
188-
return popTypes(ErrorLoc, valTypeToStackType(ValTypes), ExactMatch);
195+
return popTypes(ErrorLoc, valTypesToStackTypes(ValTypes), ExactMatch);
189196
}
190197

191198
bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
@@ -215,7 +222,7 @@ bool WebAssemblyAsmTypeCheck::popAnyType(SMLoc ErrorLoc) {
215222
}
216223

217224
void WebAssemblyAsmTypeCheck::pushTypes(ArrayRef<wasm::ValType> ValTypes) {
218-
Stack.append(valTypeToStackType(ValTypes));
225+
Stack.append(valTypesToStackTypes(ValTypes));
219226
}
220227

221228
bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCOperand &LocalOp,
@@ -322,6 +329,63 @@ bool WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc, bool ExactMatch) {
322329
return checkTypes(ErrorLoc, FuncInfo.Sig.Returns, ExactMatch);
323330
}
324331

332+
// Unlike checkTypes() family, this just compare the equivalence of the two
333+
// ValType vectors
334+
static bool compareTypes(ArrayRef<wasm::ValType> TypesA,
335+
ArrayRef<wasm::ValType> TypesB) {
336+
if (TypesA.size() != TypesB.size())
337+
return true;
338+
for (size_t I = 0, E = TypesA.size(); I < E; I++)
339+
if (TypesA[I] != TypesB[I])
340+
return true;
341+
return false;
342+
}
343+
344+
bool WebAssemblyAsmTypeCheck::checkTryTable(SMLoc ErrorLoc,
345+
const MCInst &Inst) {
346+
bool Error = false;
347+
unsigned OpIdx = 1; // OpIdx 0 is the block type
348+
int64_t NumCatches = Inst.getOperand(OpIdx++).getImm();
349+
for (int64_t I = 0; I < NumCatches; I++) {
350+
int64_t Opcode = Inst.getOperand(OpIdx++).getImm();
351+
std::string ErrorMsgBase =
352+
"try_table: catch index " + std::to_string(I) + ": ";
353+
354+
const wasm::WasmSignature *Sig = nullptr;
355+
SmallVector<wasm::ValType> SentTypes;
356+
if (Opcode == wasm::WASM_OPCODE_CATCH ||
357+
Opcode == wasm::WASM_OPCODE_CATCH_REF) {
358+
if (!getSignature(ErrorLoc, Inst.getOperand(OpIdx++),
359+
wasm::WASM_SYMBOL_TYPE_TAG, Sig))
360+
SentTypes.insert(SentTypes.end(), Sig->Params.begin(),
361+
Sig->Params.end());
362+
else
363+
Error = true;
364+
}
365+
if (Opcode == wasm::WASM_OPCODE_CATCH_REF ||
366+
Opcode == wasm::WASM_OPCODE_CATCH_ALL_REF) {
367+
SentTypes.push_back(wasm::ValType::EXNREF);
368+
}
369+
370+
unsigned Level = Inst.getOperand(OpIdx++).getImm();
371+
if (Level < BlockInfoStack.size()) {
372+
const auto &DestBlockInfo =
373+
BlockInfoStack[BlockInfoStack.size() - Level - 1];
374+
if (compareTypes(SentTypes, DestBlockInfo.Sig.Returns)) {
375+
std::string ErrorMsg =
376+
ErrorMsgBase + "type mismatch, catch tag type is " +
377+
getTypesString(SentTypes) + ", but destination's type is " +
378+
getTypesString(DestBlockInfo.Sig.Returns);
379+
Error |= typeError(ErrorLoc, ErrorMsg);
380+
}
381+
} else {
382+
Error = typeError(ErrorLoc, ErrorMsgBase + "invalid depth " +
383+
std::to_string(Level));
384+
}
385+
}
386+
return Error;
387+
}
388+
325389
bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
326390
OperandVector &Operands) {
327391
auto Opc = Inst.getOpcode();
@@ -460,10 +524,13 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
460524
return popType(ErrorLoc, Any{});
461525
}
462526

463-
if (Name == "block" || Name == "loop" || Name == "if" || Name == "try") {
527+
if (Name == "block" || Name == "loop" || Name == "if" || Name == "try" ||
528+
Name == "try_table") {
464529
bool Error = Name == "if" && popType(ErrorLoc, wasm::ValType::I32);
465530
// Pop block input parameters and check their types are correct
466531
Error |= popTypes(ErrorLoc, LastSig.Params);
532+
if (Name == "try_table")
533+
Error |= checkTryTable(ErrorLoc, Inst);
467534
// Push a new block info
468535
BlockInfoStack.push_back({LastSig, Stack.size(), Name == "loop"});
469536
// Push back block input parameters
@@ -472,8 +539,8 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
472539
}
473540

474541
if (Name == "end_block" || Name == "end_loop" || Name == "end_if" ||
475-
Name == "end_try" || Name == "delegate" || Name == "else" ||
476-
Name == "catch" || Name == "catch_all") {
542+
Name == "end_try" || Name == "delegate" || Name == "end_try_table" ||
543+
Name == "else" || Name == "catch" || Name == "catch_all") {
477544
assert(!BlockInfoStack.empty());
478545
// Check if the types on the stack match with the block return type
479546
const auto &LastBlockInfo = BlockInfoStack.back();
@@ -586,6 +653,12 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
586653
return Error;
587654
}
588655

656+
if (Name == "throw_ref") {
657+
bool Error = popType(ErrorLoc, wasm::ValType::EXNREF);
658+
pushType(Polymorphic{});
659+
return Error;
660+
}
661+
589662
// The current instruction is a stack instruction which doesn't have
590663
// explicit operands that indicate push/pop types, so we get those from
591664
// the register version of the same instruction.

llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@ class WebAssemblyAsmTypeCheck final {
6565
void pushTypes(ArrayRef<wasm::ValType> Types);
6666
void pushType(StackType Type) { Stack.push_back(Type); }
6767
bool match(StackType TypeA, StackType TypeB);
68-
std::string getTypesString(ArrayRef<StackType> Types, size_t StartPos);
68+
std::string getTypesString(ArrayRef<wasm::ValType> Types,
69+
size_t StartPos = 0);
70+
std::string getTypesString(ArrayRef<StackType> Types, size_t StartPos = 0);
6971
SmallVector<StackType, 4>
70-
valTypeToStackType(ArrayRef<wasm::ValType> ValTypes);
72+
valTypesToStackTypes(ArrayRef<wasm::ValType> ValTypes);
7173

7274
void dumpTypeStack(Twine Msg);
7375
bool typeError(SMLoc ErrorLoc, const Twine &Msg);
@@ -80,6 +82,7 @@ class WebAssemblyAsmTypeCheck final {
8082
bool getTable(SMLoc ErrorLoc, const MCOperand &TableOp, wasm::ValType &Type);
8183
bool getSignature(SMLoc ErrorLoc, const MCOperand &SigOp,
8284
wasm::WasmSymbolType Type, const wasm::WasmSignature *&Sig);
85+
bool checkTryTable(SMLoc ErrorLoc, const MCInst &Inst);
8386

8487
public:
8588
WebAssemblyAsmTypeCheck(MCAsmParser &Parser, const MCInstrInfo &MII,

llvm/test/MC/WebAssembly/annotations.s

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
test_annotation:
1010
.functype test_annotation () -> ()
1111
.tagtype __cpp_exception i32
12+
.tagtype __c_longjmp i32
1213
try
1314
br 0
1415
catch __cpp_exception

llvm/test/MC/WebAssembly/eh-assembly.s

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+exception-handling --no-type-check < %s | FileCheck %s
1+
# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+exception-handling < %s | FileCheck %s
22
# Check that it converts to .o without errors, but don't check any output:
3-
# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+exception-handling --no-type-check -o %t.o < %s
3+
# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+exception-handling -o %t.o < %s
44

55
.tagtype __cpp_exception i32
66
.tagtype __c_longjmp i32
@@ -24,7 +24,6 @@ eh_test:
2424
return
2525
end_block
2626
throw_ref
27-
drop
2827
end_block
2928
return
3029
end_block
@@ -101,7 +100,6 @@ eh_test:
101100
# CHECK-NEXT: return
102101
# CHECK-NEXT: end_block
103102
# CHECK-NEXT: throw_ref
104-
# CHECK-NEXT: drop
105103
# CHECK-NEXT: end_block
106104
# CHECK-NEXT: return
107105
# CHECK-NEXT: end_block

llvm/test/MC/WebAssembly/type-checker-errors.s

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -944,3 +944,26 @@ block_param_and_return:
944944

945945
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [] but got [f32]
946946
end_function
947+
948+
.tagtype __cpp_exception i32
949+
950+
eh_test:
951+
.functype eh_test () -> ()
952+
block i32
953+
block i32
954+
block i32
955+
block
956+
# CHECK: :[[@LINE+4]]:11: error: try_table: catch index 0: type mismatch, catch tag type is [i32], but destination's type is []
957+
# CHECK: :[[@LINE+3]]:11: error: try_table: catch index 1: type mismatch, catch tag type is [i32, exnref], but destination's type is [i32]
958+
# CHECK: :[[@LINE+2]]:11: error: try_table: catch index 2: type mismatch, catch tag type is [], but destination's type is [i32]
959+
# CHECK: :[[@LINE+1]]:11: error: try_table: catch index 3: type mismatch, catch tag type is [exnref], but destination's type is [i32]
960+
try_table i32 (catch __cpp_exception 0) (catch_ref __cpp_exception 1) (catch_all 2) (catch_all_ref 3)
961+
# CHECK: :[[@LINE+1]]:11: error: type mismatch, expected [i32] but got []
962+
end_try_table
963+
drop
964+
end_block
965+
end_block
966+
end_block
967+
end_block
968+
drop
969+
end_function

0 commit comments

Comments
 (0)