Skip to content

Commit 7b23468

Browse files
authored
[WebAssembly] Use 'any' type in more cases in AsmTypeCheck (#110403)
Now that we support 'any' type in the value stack in the checker, this uses it in more places. When an instruction pops multiple values, rather than popping in one by one and generating multiple error messages, it adds them to a vector and pops them at once. When the type to be popped is not clear, it pops 'any', at least makes sure there are correct number of values on the stack. So for example, in case of `table.fill`, which expects `[i32 t i32]` (where t is the type of the elements in the table), it pops them at once, generating an error message like ```console error: type mismatch, expected [i32, externref, i32] but got [...] ``` In case the table is invalid so we don't know the type, it tries to pop an 'any' instead, popping whatever value there is: ```console error: type mismatch, expected [i32, any, i32] but got [...] ``` Checks done on other instructions based on the register info are already popping and pushing types in vectors, after #110094: https://github.com/llvm/llvm-project/blob/a52251675f001115b225f57362d37e92b7355ef9/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp#L515-L536 This also pushes 'any' in case the type to push is unclear. For example, `local/global.set` pushes a value of the type specified in the local or global, but in case that local or global is invalid, we push 'any' instead, which will match with whatever type. The objective of all these is not to make one instruction's error propragate continuously into subsequent instructions. This also matches Wabt's behavior. This also renames `checkAndPopTypes` to just `popTypes`, to be consistent with a single-element version `popType`. `popType(s)` also does type checks.
1 parent 27a8f00 commit 7b23468

File tree

3 files changed

+103
-58
lines changed

3 files changed

+103
-58
lines changed

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

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -88,21 +88,21 @@ bool WebAssemblyAsmTypeCheck::match(StackType TypeA, StackType TypeB) {
8888

8989
std::string WebAssemblyAsmTypeCheck::getTypesString(ArrayRef<StackType> Types,
9090
size_t StartPos) {
91-
SmallVector<std::string, 4> Reverse;
91+
SmallVector<std::string, 4> TypeStrs;
9292
for (auto I = Types.size(); I > StartPos; I--) {
9393
if (std::get_if<Any>(&Types[I - 1]))
94-
Reverse.push_back("any");
94+
TypeStrs.push_back("any");
9595
else if (std::get_if<Ref>(&Types[I - 1]))
96-
Reverse.push_back("ref");
96+
TypeStrs.push_back("ref");
9797
else
98-
Reverse.push_back(
98+
TypeStrs.push_back(
9999
WebAssembly::typeToString(std::get<wasm::ValType>(Types[I - 1])));
100100
}
101101

102102
std::stringstream SS;
103103
SS << "[";
104104
bool First = true;
105-
for (auto It = Reverse.rbegin(); It != Reverse.rend(); ++It) {
105+
for (auto It = TypeStrs.rbegin(); It != TypeStrs.rend(); ++It) {
106106
if (!First)
107107
SS << ", ";
108108
SS << *It;
@@ -159,15 +159,15 @@ bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
159159
getTypesString(Stack, StackStartPos));
160160
}
161161

162-
bool WebAssemblyAsmTypeCheck::checkAndPopTypes(SMLoc ErrorLoc,
163-
ArrayRef<wasm::ValType> ValTypes,
164-
bool ExactMatch) {
165-
return checkAndPopTypes(ErrorLoc, valTypeToStackType(ValTypes), ExactMatch);
162+
bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
163+
ArrayRef<wasm::ValType> ValTypes,
164+
bool ExactMatch) {
165+
return popTypes(ErrorLoc, valTypeToStackType(ValTypes), ExactMatch);
166166
}
167167

168-
bool WebAssemblyAsmTypeCheck::checkAndPopTypes(SMLoc ErrorLoc,
169-
ArrayRef<StackType> Types,
170-
bool ExactMatch) {
168+
bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
169+
ArrayRef<StackType> Types,
170+
bool ExactMatch) {
171171
bool Error = checkTypes(ErrorLoc, Types, ExactMatch);
172172
auto NumPops = std::min(Stack.size(), Types.size());
173173
for (size_t I = 0, E = NumPops; I != E; I++)
@@ -176,7 +176,7 @@ bool WebAssemblyAsmTypeCheck::checkAndPopTypes(SMLoc ErrorLoc,
176176
}
177177

178178
bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc, StackType Type) {
179-
return checkAndPopTypes(ErrorLoc, {Type}, false);
179+
return popTypes(ErrorLoc, {Type});
180180
}
181181

182182
bool WebAssemblyAsmTypeCheck::popRefType(SMLoc ErrorLoc) {
@@ -207,7 +207,7 @@ bool WebAssemblyAsmTypeCheck::checkBr(SMLoc ErrorLoc, size_t Level) {
207207
StringRef("br: invalid depth ") + std::to_string(Level));
208208
const SmallVector<wasm::ValType, 4> &Expected =
209209
BrStack[BrStack.size() - Level - 1];
210-
return checkTypes(ErrorLoc, Expected, false);
210+
return checkTypes(ErrorLoc, Expected);
211211
return false;
212212
}
213213

@@ -216,13 +216,13 @@ bool WebAssemblyAsmTypeCheck::checkEnd(SMLoc ErrorLoc, bool PopVals) {
216216
BrStack.pop_back();
217217

218218
if (PopVals)
219-
return checkAndPopTypes(ErrorLoc, LastSig.Returns, false);
220-
return checkTypes(ErrorLoc, LastSig.Returns, false);
219+
return popTypes(ErrorLoc, LastSig.Returns);
220+
return checkTypes(ErrorLoc, LastSig.Returns);
221221
}
222222

223223
bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc,
224224
const wasm::WasmSignature &Sig) {
225-
bool Error = checkAndPopTypes(ErrorLoc, Sig.Params, false);
225+
bool Error = popTypes(ErrorLoc, Sig.Params);
226226
pushTypes(Sig.Returns);
227227
return Error;
228228
}
@@ -309,7 +309,7 @@ bool WebAssemblyAsmTypeCheck::getSignature(SMLoc ErrorLoc,
309309
}
310310

311311
bool WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc, bool ExactMatch) {
312-
bool Error = checkAndPopTypes(ErrorLoc, ReturnTypes, ExactMatch);
312+
bool Error = popTypes(ErrorLoc, ReturnTypes, ExactMatch);
313313
Unreachable = true;
314314
return Error;
315315
}
@@ -326,12 +326,14 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
326326
pushType(Type);
327327
return false;
328328
}
329+
pushType(Any{});
329330
return true;
330331
}
331332

332333
if (Name == "local.set") {
333334
if (!getLocal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
334335
return popType(ErrorLoc, Type);
336+
popType(ErrorLoc, Any{});
335337
return true;
336338
}
337339

@@ -341,6 +343,8 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
341343
pushType(Type);
342344
return Error;
343345
}
346+
popType(ErrorLoc, Any{});
347+
pushType(Any{});
344348
return true;
345349
}
346350

@@ -349,12 +353,14 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
349353
pushType(Type);
350354
return false;
351355
}
356+
pushType(Any{});
352357
return true;
353358
}
354359

355360
if (Name == "global.set") {
356361
if (!getGlobal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
357362
return popType(ErrorLoc, Type);
363+
popType(ErrorLoc, Any{});
358364
return true;
359365
}
360366

@@ -364,16 +370,21 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
364370
pushType(Type);
365371
return Error;
366372
}
373+
pushType(Any{});
367374
return true;
368375
}
369376

370377
if (Name == "table.set") {
371378
bool Error = false;
372-
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
373-
Error |= popType(ErrorLoc, Type);
374-
else
379+
SmallVector<StackType, 2> PopTypes;
380+
PopTypes.push_back(wasm::ValType::I32);
381+
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
382+
PopTypes.push_back(Type);
383+
} else {
375384
Error = true;
376-
Error |= popType(ErrorLoc, wasm::ValType::I32);
385+
PopTypes.push_back(Any{});
386+
}
387+
Error |= popTypes(ErrorLoc, PopTypes);
377388
return Error;
378389
}
379390

@@ -384,22 +395,32 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
384395
}
385396

386397
if (Name == "table.grow") {
387-
bool Error = popType(ErrorLoc, wasm::ValType::I32);
388-
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
389-
Error |= popType(ErrorLoc, Type);
390-
else
398+
bool Error = false;
399+
SmallVector<StackType, 2> PopTypes;
400+
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
401+
PopTypes.push_back(Type);
402+
} else {
391403
Error = true;
404+
PopTypes.push_back(Any{});
405+
}
406+
PopTypes.push_back(wasm::ValType::I32);
407+
Error |= popTypes(ErrorLoc, PopTypes);
392408
pushType(wasm::ValType::I32);
393409
return Error;
394410
}
395411

396412
if (Name == "table.fill") {
397-
bool Error = popType(ErrorLoc, wasm::ValType::I32);
398-
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
399-
Error |= popType(ErrorLoc, Type);
400-
else
413+
bool Error = false;
414+
SmallVector<StackType, 2> PopTypes;
415+
PopTypes.push_back(wasm::ValType::I32);
416+
if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
417+
PopTypes.push_back(Type);
418+
} else {
401419
Error = true;
402-
Error |= popType(ErrorLoc, wasm::ValType::I32);
420+
PopTypes.push_back(Any{});
421+
}
422+
PopTypes.push_back(wasm::ValType::I32);
423+
Error |= popTypes(ErrorLoc, PopTypes);
403424
return Error;
404425
}
405426

@@ -525,7 +546,7 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
525546
if (Op.OperandType == MCOI::OPERAND_REGISTER)
526547
PopTypes.push_back(WebAssembly::regClassToValType(Op.RegClass));
527548
}
528-
bool Error = checkAndPopTypes(ErrorLoc, PopTypes, false);
549+
bool Error = popTypes(ErrorLoc, PopTypes);
529550
SmallVector<wasm::ValType, 4> PushTypes;
530551
// Now push all the defs onto the stack.
531552
for (unsigned I = 0; I < II.getNumDefs(); I++) {

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,21 @@ class WebAssemblyAsmTypeCheck final {
4040
bool Unreachable = false;
4141
bool Is64;
4242

43+
// checkTypes checks 'Types' against the value stack. popTypes checks 'Types'
44+
// against the value stack and also pops them.
45+
//
4346
// If ExactMatch is true, 'Types' will be compared against not only the top of
4447
// the value stack but the whole remaining value stack
4548
// (TODO: This should be the whole remaining value stack "at the the current
4649
// block level", which has not been implemented yet)
4750
bool checkTypes(SMLoc ErrorLoc, ArrayRef<wasm::ValType> Types,
48-
bool ExactMatch);
49-
bool checkTypes(SMLoc ErrorLoc, ArrayRef<StackType> Types, bool ExactMatch);
50-
bool checkAndPopTypes(SMLoc ErrorLoc, ArrayRef<wasm::ValType> Types,
51-
bool ExactMatch);
52-
bool checkAndPopTypes(SMLoc ErrorLoc, ArrayRef<StackType> Types,
53-
bool ExactMatch);
51+
bool ExactMatch = false);
52+
bool checkTypes(SMLoc ErrorLoc, ArrayRef<StackType> Types,
53+
bool ExactMatch = false);
54+
bool popTypes(SMLoc ErrorLoc, ArrayRef<wasm::ValType> Types,
55+
bool ExactMatch = false);
56+
bool popTypes(SMLoc ErrorLoc, ArrayRef<StackType> Types,
57+
bool ExactMatch = false);
5458
bool popType(SMLoc ErrorLoc, StackType Type);
5559
bool popRefType(SMLoc ErrorLoc);
5660
bool popAnyType(SMLoc ErrorLoc);

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

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -139,31 +139,30 @@ table_set_missing_tabletype:
139139

140140
table_set_empty_stack_while_popping_1:
141141
.functype table_set_empty_stack_while_popping_1 () -> ()
142-
# CHECK: :[[@LINE+2]]:3: error: type mismatch, expected [externref] but got []
143-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
142+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref] but got []
144143
table.set valid_table
145144
end_function
146145

147146
table_set_empty_stack_while_popping_2:
148147
.functype table_set_empty_stack_while_popping_2 (externref) -> ()
149148
local.get 0
150-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
149+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref] but got [externref]
151150
table.set valid_table
152151
end_function
153152

154153
table_set_type_mismatch_1:
155154
.functype table_set_type_mismatch_1 () -> ()
156155
i32.const 0
157156
ref.null_func
158-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [externref] but got [funcref]
157+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref] but got [i32, funcref]
159158
table.set valid_table
160159
end_function
161160

162161
table_set_type_mismatch_2:
163162
.functype table_set_type_mismatch_2 () -> ()
164163
f32.const 1.0
165164
ref.null_extern
166-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got [f32]
165+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref] but got [f32, externref]
167166
table.set valid_table
168167
end_function
169168

@@ -187,25 +186,22 @@ table_fill_missing_tabletype:
187186

188187
table_fill_empty_stack_while_popping_1:
189188
.functype table_fill_empty_stack_while_popping_1 () -> ()
190-
# CHECK: :[[@LINE+3]]:3: error: type mismatch, expected [i32] but got []
191-
# CHECK: :[[@LINE+2]]:3: error: type mismatch, expected [externref] but got []
192-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
189+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got []
193190
table.fill valid_table
194191
end_function
195192

196193
table_fill_empty_stack_while_popping_2:
197194
.functype table_fill_empty_stack_while_popping_2 (i32) -> ()
198195
local.get 0
199-
# CHECK: :[[@LINE+2]]:3: error: type mismatch, expected [externref] but got []
200-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
196+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [i32]
201197
table.fill valid_table
202198
end_function
203199

204200
table_fill_empty_stack_while_popping_3:
205201
.functype table_fill_empty_stack_while_popping_3 (i32, externref) -> ()
206202
local.get 1
207203
local.get 0
208-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
204+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [externref, i32]
209205
table.fill valid_table
210206
end_function
211207

@@ -214,7 +210,7 @@ table_fill_type_mismatch_1:
214210
i32.const 0
215211
ref.null_extern
216212
ref.null_func
217-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got [funcref]
213+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [i32, externref, funcref]
218214
table.fill valid_table
219215
end_function
220216

@@ -223,7 +219,7 @@ table_fill_type_mismatch_2:
223219
i32.const 0
224220
ref.null_func
225221
i32.const 1
226-
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [externref] but got [funcref]
222+
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [i32, funcref, i32]
227223
table.fill valid_table
228224
end_function
229225

@@ -232,7 +228,7 @@ table_fill_type_mismatch_3:
232228
f32.const 2.0
233229
ref.null_extern
234230
i32.const 1
235-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got [f32]
231+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [f32, externref, i32]
236232
table.fill valid_table
237233
end_function
238234

@@ -241,7 +237,7 @@ table_fill_type_mismatch_4:
241237
i32.const 1
242238
ref.null_exn
243239
i32.const 1
244-
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [externref] but got [exnref]
240+
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [i32, exnref, i32]
245241
table.fill valid_table
246242
end_function
247243

@@ -256,15 +252,15 @@ table_grow_non_exist_table:
256252
table_grow_type_mismatch_1:
257253
.functype table_grow_type_mismatch_1 (externref, i32) -> (i32)
258254
local.get 1
259-
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [externref] but got []
255+
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [externref, i32] but got [i32]
260256
table.grow valid_table
261257
end_function
262258

263259
table_grow_type_mismatch_2:
264260
.functype table_grow_type_mismatch_2 (externref, i32) -> (i32)
265261
local.get 0
266262
local.get 0
267-
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [i32] but got [externref]
263+
# CHECK: [[@LINE+1]]:3: error: type mismatch, expected [externref, i32] but got [externref, externref]
268264
table.grow valid_table
269265
end_function
270266

@@ -883,9 +879,7 @@ multiple_errors_in_function:
883879
# CHECK: :[[@LINE+1]]:13: error: expected expression operand
884880
table.get 1
885881

886-
# CHECK: :[[@LINE+3]]:3: error: type mismatch, expected [i32] but got []
887-
# CHECK: :[[@LINE+2]]:3: error: type mismatch, expected [externref] but got []
888-
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got []
882+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32, externref, i32] but got [any]
889883
table.fill valid_table
890884

891885
f32.const 0.0
@@ -905,3 +899,29 @@ call_with_multi_param_and_return:
905899
call take_and_return_multi
906900
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [i32] but got [i32, i64, f32, f64]
907901
end_function
902+
903+
.functype callee (f32, i32) -> ()
904+
905+
any_value_on_stack:
906+
.functype any_value_on_stack () -> ()
907+
# This local does not exist so it should error out, but it should put an 'any'
908+
# value on the stack so 'call callee' should not error out again
909+
# CHECK: :[[@LINE+1]]:13: error: no local type specified for index 0
910+
local.get 0
911+
i32.const 0
912+
# CHECK-NOT: :[[@LINE+1]]:3: error: type mismatch
913+
call callee
914+
915+
# But this time 'call callee' should error out
916+
i32.const 0
917+
# CHECK: :[[@LINE+1]]:13: error: no local type specified for index 0
918+
local.get 0
919+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [f32, i32] but got [i32, any]
920+
call callee
921+
922+
# CHECK: :[[@LINE+2]]:13: error: no local type specified for index 0
923+
# CHECK: :[[@LINE+1]]:3: error: type mismatch, expected [any] but got []
924+
local.set 0
925+
drop
926+
927+
end_function

0 commit comments

Comments
 (0)